ACC SHELL

Path : /srv/www/vhosts/agroing/web/helpers/form_h/
File Upload :
Current File : /srv/www/vhosts/agroing/web/helpers/form_h/form_helper.php

<?php
/*
 * @version  1.2
 * @author Petr Heralecky
 *
 * todo: transform2str funguje špatně při posílání pole
 */
class Form
{
	public static $img_path = "form/formfiles/images/";
	public static $img_app_path = "form/formfiles/images/";
	public static $ajax_path = "form/formfiles/ajax.php?form=odeslano";
	public static $log_tool = NULL; // connect validity error reporting with flash-messages
	// example: Form::$log_tool = Tools::get_instance();   (my Tools class is useable)
	public static $charset = "UTF-8";
	public static $captcha_anti_alias = false; // better captcha image, but slower
	public static $changing_checkbox_classes = false; // true = umozni zmenu class i radiobuttonum a checkboxum (required...)
	public static $ajax = true; // aktivuje ajax (prida onBlur even)
	public static $loading_text = "Zpracovávám..."; // prazdne => vůbec se ne vypíše
    public static $method = "post";
	public static $escape_quotes = false;
	public static $escaped_characters = array("\\","'",'"'); // this chars will be returned width \ in get_data() ... "\" take like first!
	public $captcha = true; // provede zakladni javascript kontrolu humanoity
	public $post_by_only_submit = true; // true = odeslani formulare jde jenom submitem (ne javascriptem)
	public $no_error_names = array(); // pole prvku, u kterych se nebudou vypisovat error hlasky (ty budou pristupne jen pres metodu not_valid)
	public $not_valid_data = array(); // array whichones are not valid
	public $elements; //ulozene elementy tohoto formulre v sessnu (nesahat)
	public $data = array(); // default data in forms
	public $form_data = array(); // posted data ... attention that $_POST[a][1] = $this->form_data[a[1]]
    public static $errors_decorator = '%input%<span class="error_msg">%msg%</span>';


	protected static $instance;
	protected static $errors_near_input = false; // true = error hlasky se budou vypisovat pod inputy
	protected $form_name = "";
    protected $session_name = "";
    protected $errors_saved = false;
    protected $errors = array(); // seznam erroru... jsou k dostani pomoci get_errors()
    protected $ses; // session pro ukadani potrebných dat formulare
	protected $ses_captcha; // session ukladajici text captchy (session se prideluje v constructu)
    protected $array_abacus = array(); // nejake pocitadlo
    protected $validators;
	protected $save_errors = true;
	protected $options = array(); // static variables which shoult be rewrite to this value in stop() method

    /**
	 * vytvori formular, nastartuje sessions
	 *
	 * @param string $namespace identifikator formulare
	 * @param array $options ...	there can be each of pubic variables, for example:
	 *								data => array of values (for edit)
	 *								img_path => /base/img/  (for captcha)
	 *								img_app_path => ./img/
	 *								ajax_path => "/Form/ajax.php" or "/ajax/form"; (can be controller-action)
	 *								method => post/get
	 *							this variables will be rewrite in method stop()!
	 */
    public function __construct($namespace,$options = array()){
		if(strpos($namespace," ")){
			$this->log("wrong form name [Form - __construct()]","critical");
		}
        if(!isset($_SESSION)) session_start();
        require_once 'validators.php';
        $this->validators = new FormValidators; // instance pro volani validatoru
        $this->session_name = 'f_'.$namespace;
		$this->form_name = $namespace;
		$this->options = $options;
		foreach($options as $var=>$option){
			$this->set($var,$option,true);		// set variables from option parameter
		}
		if((self::$method=='post' && empty($_POST))||(self::$method=='get' && empty($_GET))) unset($_SESSION[$this->session_name]);
		$this->ses = &$_SESSION[$this->session_name];
		
		$this->ses_captcha = &$_SESSION['captcha_str']; // misto pro ukladani overovaciho textu - tuto session pouziu i ve validatoru
		$this->elements  = $this->ses['elements'];  // vytazeni jiz ulozenych elementu ze sesny
    }

	/**
     * Singleton instance
     *
     * @return Form (self)
     */
    public static function get_instance($namespace)
    {
        if (null === self::$instance) {
            self::$instance = new self($namespace);
        }

        return self::$instance;
    }

	public function load_data($data){  // Myslim ze jasne...
		if(is_array($data)){
			$transformed = $this->transform2str($data);
			foreach($transformed as $i => $d){
				$this->data[$i] = @htmlentities($d, ENT_QUOTES,self::$charset);
			}
		}
	}

	/**
	 * Vypise neco jako <form name="m-form" ....
	 *
	 * @param string $action
	 * @param string $other_params example: 'id="my-form" class="formular"'
	 */
	public function start($action="?odeslano=ano",$other_params=""){
		foreach($this->options as $var=>$option){
			$this->set($var,$option); // set variables and rewrite static variables in $this->options
		}
		echo '<form method="'.self::$method.'" name="'.$this->form_name.'" action="'.$action.'" enctype="multipart/form-data" class="m-form m-form-'.$this->form_name.'" '.$other_params.'>';
		if(self::$loading_text){
			echo '<div id="form-loading-'.$this->form_name.'" class="form-loading"><div><span>'.self::$loading_text.'</span></div></div>';
		}
	}

	/**
	 *
	 */
	public function stop(){
		echo '</form>';
		foreach($this->options as $var=>$option){
			$this->set($var,$option); // set variables back
		}
	}

    /**
	 * Prekouse formularovy tag s parametry zadanymi primov nem a prepise s odpovidajici value a class
	 *
     * @param string $tag zachycuje html tag a na konci jej vyplivne se spravnyma hodnotama
     * @param bool $requiredOrOptions required/not
     * @param array $requiredOrOptions napr: array('cz'=>'cestina','rus'=>'rustina')
     * @param array/string $validators obsahuje jeden ci vice validatoru/filtru
	 * @param array $for_validator predane do validatoru
	 * @param bool $print if tag should be echo in the end
	 *
	 * @return string printed value
     */
    public function _($tag, $requiredOrOptions = 0, $validators = "", $for_validator=array(), $print=true){
   /********************     zpracovani tagu      ************************/
    $tag = str_replace("\n","%break%",$tag); // nahrada odradkovani (kvuli preg_replace)
		$tag_lower = strtolower($tag);
    $name = preg_replace("#(.*)(name *= *(\"|\')([^\"\']*)(.*))#e","'$4'",$tag_lower);  // separuje name
		$onBlur = preg_replace("#(.*)(onblur *= *(\"|\')([^\"]*)(.*))#e","'$4'",$tag_lower);  // separuje onBlur
		$onKeyUp = preg_replace("#(.*)(onkeyup *= *(\"|\')([^\"]*)(.*))#e","'$4'",$tag_lower);  // separuje onKeyUp
		$onChange = preg_replace("#(.*)(onchange *= *(\"|\')([^\"]*)(.*))#e","'$4'",$tag_lower);  // separuje onChange
		$onMouseUp = preg_replace("#(.*)(onmouseup *= *(\"|\')([^\"]*)(.*))#e","'$4'",$tag_lower);  // separuje onMouseup
		$onClick = preg_replace("#(.*)(onclick *= *(\"|\')([^\"]*)(.*))#e","'$4'",$tag_lower);  // separuje onClick
    $class = preg_replace("#(.*)(class *= *(\"|\')([^\"\']*)(.*))#e","'$4'",$tag_lower);// separuje class
    $tagname = preg_replace("#( *< *)([^ ]*)(.*)#e","'$2'",$tag_lower);// separuje jmeno tagu
		$checked=(strpos($tag_lower, 'checked') ? true : false);
		if(stripos($tag, 'multiple')) $multiple = true;
		else $multiple = false;
        if(stripos($tagname, 'textarea')!==false) $tagname = "textarea"; // doladeni u textarey
        $required = $requiredOrOptions;
		$value=false;
        if($tagname=="input"){
            $type = preg_replace("#(.*)(type *= *(\"|\')([^\"\']*)(.*))#e","'$4'",$tag_lower);  // separuje typ
            $value = preg_replace("#(.*)(value *= *(\"|\')([^\"\']*)(.*))#e","'$4'",$tag_lower);// separuje value
        }elseif($tagname == "select"){
            $options = $requiredOrOptions;
            $required = 0;
            $type = $tagname;
        }elseif($tagname == "textarea"){
            $type = $tagname;
        }else{
            $this->log("wrong syntax [Form - _()]".$tagname."-","critical");
        }

        // pokud nebyli nalezeny, prepisou se na false
        if($name==$tag_lower) $name=false;			if($type==$tag_lower){ $type="text"; $type_miss=true; }
        if(@$value==$tag_lower) $value=false;		if($class==$tag_lower) $class=false;
		if($onBlur==$tag_lower) $onBlur=false;		if($onKeyUp==$tag_lower) $onKeyUp=false;
		if($onChange==$tag_lower) $onChange=false;	if($onMouseUp==$tag_lower) $onMouseUp=false;
		if($onClick==$tag_lower) $onClick=false;

		// all types of buttons are rewrited to type "button" (just for needs this class)
		$submit = false;
		if(in_array($type,array('submit','button','reset','image'))){
			if($type=='submit') $submit = true;
			$type = "button";
		}

		//
		if($tagname=="textarea" && !empty($value)){
			$tag = str_replace(">".$value."<","><",$tag);
		}

        // naplneni [] cislama
        if($multiple){
            $name = str_replace('[]','',$name);
        }else{
            if(!isset($this->array_abacus[$name])) $this->array_abacus[$name] = 0;
			$old_name = $name;
            $name = str_replace('[]','['. $this->array_abacus[$name]++ .']',$name);
			if($this->array_abacus[$old_name]){ // prepsani name v tagu pokud jde o array-name
				$tag = preg_replace("#((name)( *= *(\"|\'))([^\"\']*)(\"|\'))#e","'$2=\"$name\"'",$tag);
			}
        }

        if(!is_array($validators) && $validators){ // vyrobi z jednoho prvku pole pokud zatim neni
            $validators = array($validators);
        }

   /********************     doplneni chybjejicich casti do tagu      ************************/
        $dodatek = ""; // zde se ulozi chybejici class nebo value, ktere se pripadne vlozi do tagu
		if(isset($type_miss) && $type_miss){
			$dodatek .= ' type="text"';
		}
        if(!in_array($type,array('hidden','button')) && $class === false){ // pokud chybi class
            $dodatek .= ' class=""';
        }
        if(!in_array($type,array('checkbox','textarea','select','button')) && $value === false ){
            $dodatek .= ' value=""';  // pokud chybi value a neni nevaluovy prvek
        }
		if(self::$ajax && !in_array($type,array('hidden','button')) && $onBlur === false ){
            $dodatek .= ' onBlur=""';  // pokud chybi onBlur
        }
		if(self::$ajax && !in_array($type,array('hidden','select','radio','checkbox','button')) && $onKeyUp === false ){
            $dodatek .= ' onKeyUp=""';  // pokud chybi onKeyUp
        }
		if(self::$ajax && in_array($type,array('select')) && $onChange === false ){
            $dodatek .= ' onChange=""';  // pokud chybi onChange
        }
		if(self::$ajax && in_array($type,array('radio','checkbox')) && $onMouseUp === false ){
            $dodatek .= ' onMouseUp=""';  // pokud chybi onChange
        }
		if($type == "button" && $onClick === false){
            $dodatek .= ' onClick=""';  // pokud chybi onChange
        }
		// write missing parts
		if($dodatek){
			$tag = substr($tag,0,strpos($tag,' ',2)) . $dodatek . strstr(trim($tag),' ');
		}

   /********************     konec u nekterych prvku      ************************/
        if(in_array($type,array('submit','button','reset','image'))){
            if($submit){
                if(!$name){
                    $tag = substr($tag,0,strpos($tag,' ',2)) . ' name="' . $this->session_name. '"' . strstr(trim($tag),' ');
                }
				if(self::$loading_text){
					//$tag = substr($tag,0,strpos($tag,' ',2)) . ' onclick="document.getElementById(\'form-loading-'.$this->form_name.'\').style.display=\'block\';"' . strstr(trim($tag),' ');
					$onClick.=" document.getElementById(\'form-loading-".$this->form_name."\').style.display=\'block\';";
					$tag = preg_replace("#((onClick)( *= *(\"|\'))([^\"]*)(\"|\'))#e","'$2=\"$onClick\"'",$tag);
				}
                $this->ses['ok'][] = $name;
				echo $tag;
				if($this->captcha){
					$this->captcha(true);
				}
            }else{
				echo $tag;      // butony are ignored
			}
            return 1;			// a ukonci metodu
        }
        if(!$name){
            $this->log("wrong syntax, name is missing [Form - _()]","critical");
        }


	/********************     plneni tridy tagu      ************************/
        // moznost omezeni vypisu tridy u checkboxu a radia... metoda setCheckboxClasses();
        if(self::$changing_checkbox_classes || ($type != 'checkbox' && $type != 'radio')){
            if($class===false) $class = "";
            if($required){
                $class .= " required";
            }else{
                $class .= " optional";
            }
            if(isset($this->not_valid_data[$name])){
                $class .= " not_valid";
            }
            $tag = preg_replace("#((class)( *= *(\"|\'))([^\"\']*)(\"|\'))#e","'$2=\"$class\"'",$tag);
        }


   /********************     ulozeni vlastnosti do session      ************************/
		if(!empty($other_values)&&is_string($other_values)) //predela pripadne slovo na pole
		$other_values = array(is_array($other_values));

        $this->ses['elements'][$name]['type'] = $type;
		$this->ses['elements'][$name]['class'] = $class;
		$this->ses['elements'][$name]['validators'] = $validators;
		$this->ses['elements'][$name]['required'] = $required;
		$this->ses['elements'][$name]['multiple'] = $multiple;
		$this->ses['elements'][$name]['for_validator'] = $for_validator;

	/********************     pridani onBlur even - AJAX      ************************/
		if(self::$ajax){
			$onBlur = str_replace("'", "\'", $onBlur);			$onChange = str_replace("'", "\'", $onChange);
			$onMouseUp = str_replace("'", "\'", $onMouseUp);	$onKeyUp = str_replace("'", "\'", $onKeyUp);
			$onBlur.=" control_input(\'{$name}\',\'{$this->form_name}\',0,\'".self::$ajax_path."\',\'".$type."\');";
			$tag = preg_replace("#((onBlur)( *= *(\"|\'))([^\"]*)(\"|\'))#e","'$2=\"$onBlur\"'",$tag);

			if($type=="select"){
				$onChange.=" control_input(\'{$name}\',\'{$this->form_name}\',1,\'".self::$ajax_path."\',\'".$type."\');";
				$tag = preg_replace("#((onChange)( *= *(\"|\'))([^\"]*)(\"|\'))#e","'$2=\"$onChange\"'",$tag);
			}elseif(in_array($type,array('radio','checkbox'))){
				$onMouseUp.=" control_input(\'{$name}\',\'{$this->form_name}\',0,\'".self::$ajax_path."\',\'".$type."\');";
				$tag = preg_replace("#((onMouseUp)( *= *(\"|\'))([^\"]*)(\"|\'))#e","'$2=\"$onMouseUp\"'",$tag);
			}else{
				$onKeyUp.=" control_input(\'{$name}\',\'{$this->form_name}\',1,\'".self::$ajax_path."\',\'".$type."\');";
				$tag = preg_replace("#((onKeyUp)( *= *(\"|\'))([^\"]*)(\"|\'))#e","'$2=\"$onKeyUp\"'",$tag);
			}
		}

   /********************     plneni hodnot tagu      ************************/
        // predani hodnoty z postu nebo z $this->data do $post_value
        $post_value=false;
        if($this->is_post() && isset($this->form_data[$name])) $post_value = $this->form_data[$name]; // value = post
		elseif(isset($this->data[$name])) $post_value = $this->data[$name];  // value = data
		//elseif($type != 'checkbox' && $type != 'radio') $post_value = $value;    //neaktivní protože sere diakritiku
		if(is_string($post_value) && $type!="textarea") $post_value = str_replace(array('"',"'"),array("&quot;","&lsquo;"),$post_value);

		// prepsani value v tagu
        if($post_value!==false && !in_array($type,array('checkbox','textarea','select','submit','radio','reset','image','button','password'))){ // pokud ani jedno zustane hodnota v editu
			//$tag = preg_replace("#((value)( *= *(\"|\'))([^\"\']*)(\"|\'))#e","'$2=\"".str_replace(array("'",'"'),array("\\'",'\\"'),$post_value)."\"'",$tag);
					// jedna verze s uvozovkami a jedna s apostrofy
					$tag = preg_replace("#((value)( *= *(\"|\'))([^\"]*)(\"|\'))#e","'$2=\"".str_replace(array("'",'"'),array("\\'",'\\"'),$post_value)."\"'",$tag);
					$tag = preg_replace("#((value)( *= *(\'))([^\']*)(\'))#e","'$2=\"".str_replace(array("'",'"'),array("\\'",'\\"'),$post_value)."\"'",$tag);
        }elseif($post_value!==false && $type=='textarea'){
            $tag = preg_replace("#(>)(.*)(</? *textarea *>)#e","'>".str_replace("'","\\'",$post_value)."</textarea>'",$tag);
        }
		if(is_string($post_value)){
			$post_value = html_entity_decode($post_value, ENT_QUOTES,self::$charset);
		}

		$return_value = $post_value;

		// oznaceni radiobuttonu a checkboxu
		if( ($type == 'checkbox' || $type == 'radio')){ // $this->is_post() &&
			if(!empty($this->data)){
				$tag = preg_replace("#(checked)( *= *(\"|\') *checked *(\"|\'))?#e","''",$tag);
			}
			// nastavi checked
			if($type == 'checkbox' && ($post_value != false || $checked)){ //$post_value === $value
				$tag = substr($tag,0,strpos($tag,' ',2)) . ' checked="checked"' . strstr(trim($tag),' ');
			}
			if($type == 'radio' && ($post_value === $value || $checked)){ //$this->form_data[$name]===$value
				$tag = substr($tag,0,strpos($tag,' ',2)) . ' checked="checked"' . strstr(trim($tag),' ');
			}
		}

		// označení polozek select
        if($type == 'select' ){
            $tag = str_replace('</select>','',$tag);
			if(empty($options) || !is_array($options)){
				$this->log("some select tag get wrong \$options parameter [Form->_()]");
			}else{
				if(isset($options[0])){
					$tag .= '<option value="0">' . $options[0] . '</option>'; // to avoid selecting unselected options
					unset($options[0]);
				}
				foreach($options as $val=>$cont){
					$sel = '';
					if(($post_value==$val || (is_array($post_value) && in_array($val,$post_value))) && $post_value!==false)
						$sel = ' selected="selected"';
					$tag .= '<option value="' . $val . '"' . $sel . '>' . $cont . '</option>';

				}
			}
            $tag .= '</select>';
        }

        $tag = str_replace("%break%","\n",$tag); // opetovne nahrazeni odradkovani (kvuli preg_replace)

		if($print){
			echo $tag; // a konecne zpetne vypsani tagu
			echo "\n";
		}

		if(stripos($class,"datetime")!==false){
			echo $this->calendar(1,$name);
		}elseif(stripos($class,"date")!==false){
			echo $this->calendar(0,$name);
		}elseif(stripos($class,"datetimesec")!==false){
			echo $this->calendar(2,$name);
		}

		//vypis erroru primo pod input
		if(self::$errors_near_input && (empty($this->no_error_names) || !in_array($name,$this->no_error_names) && $print)){ // pokud není v zakázaných
			$this->print_under($name);
		}

		return $return_value;
    }

	/**
	 * vraci ikonku kalendare
	 * @param int $with_time 0-bez casu, 1-s hodinama a minutama, 2-i sekundy
	 * @param string $name jmeno inputu
	 * @return string ... html tag
	 */
	public function calendar($with_time,$name){
		return "";
		$result = '<a class="form-calendar" href="#" onclick="show_calendar(\'document.'.$this->form_name.'.'.$name.'\',
					document.'.$this->form_name.'.'.$name.'.value, '.$with_time.', \''.self::$img_path.'\'); return false;">';
		$result .= '	<img src="'.self::$img_path.'cal.png" width="23" height="19" alt="Klikněte pro výběr data">';
		$result .= ' </a>';
		return $result;
	}

    /**
     * zkontroluje validitu poslanych dat primo z postu a ulozi do $this->form_data[]
	 * erory ulozi do $this-errors[], z kama se daji vytahnout get_errors() nebo print_errors()
	 *
     * @return bool zda jsou data validni
     */
    public function validate($save_errors = true){
		$this->save_errors = $save_errors;
        if(!$this->is_post()){
			$this->log("Try to validate, but it not posted [Form->validate()]","debug");
            return false;  // formular nebyl odeslan -> not valid
        }
		if(!empty($this->errors)){
			$result = false; // navratova hodnota jesli jsou data validni
		}else{
			$result = true;
		}
        if(self::$method=="post") $d = $_POST;
        else $d = $_GET;

        $d = $this->transform2str($d);

        if(!empty($_FILES)){ // prevzedni jmen inputu typu "file"
            foreach($_FILES as $i=>$file){
                $d[$i] = $file['tmp_name'];
            }
        }
        foreach($this->elements as $index=>$element){
            if(!isset($d[$index])){
				if(isset($element['multiple']) && $element['multiple']){
					$d = $this->mult_transform($d,$index);
				}else{
					$d[$index] = false;
				}
            }

			if(isset($element['multiple']) && $element['multiple']){
				$d = $this->mult_transform($d,$index);
			}

			if($element['type']=='checkbox' && $d[$index]){
				$d[$index] = 1;
			}

			if($element['type']=='file'){
				if(isset($_FILES[$index])) $value = $_FILES[$index];
				else $value = "";
			}else{
				$value = &$d[$index];
			}
			if(!$this->validate_one($value,$index,false)){ // $element['for_validator']
				$result = false;
			}

            // ulozi data z postu do $form_data
            if(isset($_FILES[$index])){
                $this->form_data[$index] = $_FILES[$index];
            }else{
                $this->form_data[$index] = $d[$index];
            }

        }
        $this->errors_saved = true;
		if(self::$log_tool){
			$this->flash_errors();
		}

        return $result;
    }

	/**
	 * Zkontroluje validitu 1 prvku (pri pouziti filtru muze value take pozmenit).
	 * Error se ulozi metoda save_error().
	 * @param string &$value hodnota prvku (muze byt zmenena)
	 * @param string $index name prvku
	 * @return bool validni/nevalidni
	 */
	public function validate_one(&$value,$index,$ajax=false){
		$element = $this->elements[$index];
		if(is_array($value) && !empty($value)){
			$value_bez_mezer = 'array';
		}else{
			$value_bez_mezer = @trim($value);
		}
		if(!is_array($this->elements[$index])) return true;
		if($ajax) $this->elements[$index]['for_validator']['ajax'] = true;
		else{ $this->elements[$index]['for_validator']['ajax'] = false;	}
		// cyklus pro zjistni zda nebyl uveden validator s "empty" => potom se preskakuje cast pro required
		$preskoc_required = false;
		if(!empty($element['validators'])){
			foreach($element['validators'] as $val){
				if(stripos($val, 'empty')!==false) $preskoc_required = true;
			}
		}
		if($element['required'] && ($value_bez_mezer==="" || $value_bez_mezer===false || ($element['type']=='file'&&!$_FILES[$index]['tmp_name'])) && !$preskoc_required){
			if($element['type']=='file'){ // pridani eroru pro Povinne polozky (empty v FormValidators.php)
				$this->save_error('emptyFile',$index);
			}else{
				$this->save_error('empty',$index);
			}
			return false;
		}elseif($element['type']=='file' &&
				!in_array($_FILES[$index]['error'],array(UPLOAD_ERR_OK,UPLOAD_ERR_NO_FILE))){
			$this->save_error('fileError',$index);
			return false;
		}else{			// pokud prosel pres required vetvi muze se validovat
			if(!empty($element['validators'])){
				$validators = $this->validators; // instance pro volani validatoru
				foreach($this->elements[$index]['validators'] as $val){    //pruchod pres validatory
						// pokud neni validni vrati false
					if(	($value || $value==="0" || stripos($val, 'empty')!==false)
						&& !$validators->$val($value,$this->elements[$index]['for_validator'])
						&& !(isset($value['tmp_name']) && $value['tmp_name']=="")){
						// prida odpovidajici eror z FormValidators
						if(!empty($validators->emsg[$val])){
							$this->save_error($val,$index);
						}else{
							$this->save_error('%empty%',$index);
						}
						return false;
					}
				}
			}
		}
		return true;
    }

	/**
	 * @return array of errors for next html stylize
	 */
    public function get_errors(){
        if(!empty($this->elements)){
            if(!$this->errors_saved){
                $this->validate();
            }
			foreach($this->errors as $index=>$error){
				if($error=="%empty%")
					unset($this->errors[$index]);
			}
			return $this->errors;
        }
    }

	public function extra_validator($condition,$emsg,$index = ""){
		if($condition){
			$this->save_error($emsg,$index);
			if(self::$log_tool){
				$this->flash_errors();
			}
		}
	}

	/**
	 * sets dynamic and static variables and rewrites $this->options by
	 *
	 * @param string $variable name of public variable which should be set... allowed are these: "img_path","img_app_path","ajax_path","data","method","ajax","captcha","errors_near_input","changing_checkbox_classes","post_by_only_submit","no_error_names","msg","loading_text"
	 * @param string $value ...
	 */
	public function set($variable,$value,$without_static=false){
		$possible_options = array("img_path","img_app_path","ajax_path","data","method","ajax","captcha","errors_near_input","changing_checkbox_classes","post_by_only_submit","no_error_names","msg","loading_text");
		if(in_array($variable, $possible_options)){
			// start special conditions
			if($variable=="data" && is_array($value)){
				$this->load_data($value); return;
			}
			if($variable=="method" && in_array(strtolower($value),array("get","post"))){
				$value = strtolower($value);  return;
			}
			if(in_array($variable,array("img_app_path","img_path"))){ // if slash isn't in some path, add it
				if(strpos($value,"/",strlen($value)-1)!==false) $value = $value."/";  return;
			}
			// end conditions
			if(isset($this->$variable)){
				$this->$variable = $value;
				unset($variable);
			}elseif(isset(self::$$variable) && !$without_static){
				$this->options[$variable] = self::$$variable;
				self::$$variable = $value;
			}
		}else{
			$this->log("method Form::set() has wrong first parameter","critical");
		}

	}

	/**
	 * print html list of errors width "form_errors" class
	 */
	public function print_errors(){
		$errors = $this->get_errors();
		if(!empty($errors)){
			echo '<ul class="form-errors">';
			foreach($errors as $index=>$error){
				echo '<li>' . $error . '</li>';
			}
			echo '</ul>';
		}
	}

	/**
	 * gives errors to flash messager
	 */
	public function flash_errors(){
		$errors = $this->get_errors();
		if(!empty($errors)){
			foreach($errors as $index=>$error){
				$this->log($error);
			}
		}
	}

	/**
	 * logs errors e.t.c. by flashLooger
	 *
	 * @param string $msg
	 * @param string $type one of this: "succ","warn","alert","critical"
	 * @return bool
	 */
	private function log($msg,$type = "alert"){
		$type = strtolower($type);
		$types = array("succ","warn","alert","critical","debug");
		if(!in_array($type,$types)){
			$this->log("wrong parameter \$type [Form - log()]","critical");
			return false;
		}
		if(self::$log_tool && !is_object(self::$log_tool)){
			throw new Exception('Form::loghTool in not good set.');
		}
		if(is_object(self::$log_tool)){
			self::$log_tool->flash($msg,$type);
		}else{
			if($type=="critical"){
				echo "<div style='border: 2px solid red;  padding: 10px 20px'>";
				echo $msg;
				echo "<br/><small>logger is off or bad set :(</small></div>";
			}
		}
		return true;
	}

    /**
	 * nastavi tag, ve kterem bude obsazen error, ktery se napise hned za postizeny input
	 * @param string $before
	 * @param string $after
	 * @param bool $after_tag
	 */
    public static function set_errors_near_input( $enable = true,
			$before = '<span class="error_msg">', $after = '</span>', $after_tag = true){
		self::$errors_near_input = $enable;
        self::$errors_decorator = $before . '%msg%' . $after;
		if($after_tag){
			self::$errors_decorator .= "%input%";
		}else{
			self::$errors_decorator = "%input%" . self::$errors_decorator;
		}
    }

    /**
	 * @return bool jesli je formular radne odeslan a validni
	 */
    public function ready(){
		if($this->is_post() && $this->validate()){
			return true;
		}else{
			return false;
		}
    }

    /**
	 * @param bool $free_data destroy sessions of this form (default==false)
	 * @param bool $make_alerts if you want to save errors (default==true)
	 * @return array form data
	 */
    public function get_data($free_data = false,$save_errors = true) {
        if(empty($this->form_data)){
            $this->validate($save_errors);
            if(empty($this->form_data)){
                $this->log("nowhing was sent by form. Maybe no elements was defined [Form - get_data()]","critical");
                return false;
            }
        }
		$data = $this->form_data;
		if($free_data){
			$this->free();
		}
		if(self::$escape_quotes){
			foreach($data as $i=>$d){
				// this space between backshash and quotes is to avoid sql-hacking...
				$data[$i] = str_replace(array("\\'",'\\"',"'",'"'),array("\\ '",'\\ "',"\\'",'\\"'),$d);
			}
		}
		$data = $this->transform2array($data);
        return $data;
    }

	/**
	 * free session data
	 */
	public function free(){
		unset($_SESSION[$this->session_name]);
	}

	/**
	 * Vrací true pokud byl form odeslán (ovlivneno prom post_by_only_submit)
	 * @return bool odeslan/neodeslan
	 */
    public function is_post(){
		if(empty ($this->elements)){
			return false;
		}
        if(self::$method=="post") $d = $_POST;
        else $d = $_GET;
        if( isset($d[$this->session_name]) ){  // (isset($this->ses['ok'])&&isset($d[$this->ses['ok']])) ||
            return true;
        }
		if(!empty($this->ses['ok'])) foreach($this->ses['ok'] as $ok){
			if(isset($d[$ok])) return true;
		}
        if(!$this->post_by_only_submit){ // bude brat formular za odeslany jiz pri prvnim nastavenem prvku v postu
			foreach($this->elements as $index=>$element){
				if(isset($d[$index]) && $index!="captcha"){  // pokud bude nastaveny jeden z elementu formulare v postech,
					return true;     // bude to znacit odeslani formulare
				}
			}
        }
        return false;
    }

	/**
	 * pokud prvek s uvedenym jmenem nebyl validni, vrati chybovou hlasku
	 * @param string $name jmeno validovaneho prvku
	 * @return string chybova hlaska
	 */
    public function not_valid($name){
        if(!empty($this->not_valid_data[$name])){
            return $this->not_valid_data[$name];
        }else{
            return "";
        }
    }

	/**
	 * Vrari error hlasku s prislusnym id pro ajax a prislusnym html decoratorem
	 * @param string $name jmeno validovaneho prvku
	 */
    public function print_error($name){
		echo '<span id="error_'.$name.'">';
        if($this->not_valid($name)){
            echo str_replace('%msg%', $this->not_valid_data[$name], self::$errors_decorator);
        }else{
            echo "";
        }
		echo '</span>';
    }

	/**
	 * Zkontroluje zda je uzivatel podezreli nebo ma moc pokusu a kdyztak vyrobi captcha pro kontrolu
	 * @param string $captcha_str string to transform
	 */
	public function captcha($javascript=false){
		if(strlen($this->ses_captcha)==4){
			$captcha_str = $this->ses_captcha;
		}else{
			$captcha_str = $this->rand_str(4);
		}
		// konfigurace rozměrů a pozic
		$matrix_dim = array('x' => 52, 'y' => 32);
		$captcha_dim = array('x' => 240, 'y' => 62);
		$distance = array('x' => -30, 'y' => 1, 'z' => 1);
		$metric = array('x' => 12, 'y' => 16, 'z' => 5);
		$offset = array('x' => 0, 'y' => -60);
		$cam_location = array('x' => -30, 'y' => 0, 'z' => -250);
		$cam_rotation = array('x' => -1, 'y' => 0, 'z' => 0);
		$viewer_position = array('x' => 0, 'y' => -500, 'z' => -80);
		// matrice
		$matrix = imagecreatetruecolor($matrix_dim['x'], $matrix_dim['y']);
		$black = imagecolorexact($matrix, 0, 0, 0);
		$white = imagecolorexact($matrix, 255, 255, 255);
		imagefill($matrix, 0, 0, $white);
		// font lucon
		imagefttext($matrix, 13.5, 0, 1, 16, $black, self::$img_app_path . 'lucon.ttf', $captcha_str);
		// výpočet bodů ve 3d
		$point = array();
		for ($x = 0; $x < $matrix_dim['x']; $x++) {
			for ($y = 0; $y < $matrix_dim['y']; $y++) {
				$img = $matrix;
				$c = @imagecolorat($img, $x, $y);
				if($c === false) return false;
				if(imageistruecolor($img)){
					$red = ($c >> 16) & 0xFF;
					$green = ($c >> 8) & 0xFF;
					$blue = $c & 0xFF;
				}else{
					$i = imagecolorsforindex($img, $c);
					$red = $i['red'];
					$green = $i['green'];
					$blue = $i['blue'];
				}
				$m = min($red, $green, $blue);
				$n = max($red, $green, $blue);
				$lightness = (double)(($m + $n) / 510.0);
				$p = array('x' => $x * $metric['x'] + $distance['x'], 'y' => $lightness * $metric['y'] + $distance['y'], 'z' => ($matrix_dim['y'] - $y) * $metric['z'] + $distance['z']);
				$translation = array();
				$projection = array();
				$translation['x'] = cos($cam_rotation['y']) * (sin($cam_rotation['z']) * ($p['y'] - $cam_location['y']) + cos($cam_rotation['z']) * ($p['x'] - $cam_location['x'])) - sin($cam_rotation['y']) * ($p['z'] - $cam_location['z']);
				$translation['y'] = sin($cam_rotation['x']) * (cos($cam_rotation['y']) * ($p['z'] - $cam_location['z']) + sin($cam_rotation['y']) * (sin($cam_rotation['z']) * ($p['y'] - $cam_location['y']) + cos($cam_rotation['z']) * ($p['x'] - $cam_location['x']))) + cos($cam_rotation['z']) * (cos($cam_rotation['z']) * ($p['y'] - $cam_location['y']) - sin($cam_rotation['z']) * ($p['x'] - $cam_location['x']));
				$translation['z'] = cos($cam_rotation['x']) * (cos($cam_rotation['y']) * ($p['z'] - $cam_location['z']) + sin($cam_rotation['y']) * (sin($cam_rotation['z']) * ($p['y'] - $cam_location['y']) + cos($cam_rotation['z']) * ($p['x'] - $cam_location['x']))) - sin($cam_rotation['z']) * (cos($cam_rotation['z']) * ($p['y'] - $cam_location['y']) - sin($cam_rotation['z']) * ($p['x'] - $cam_location['x']));
				$projection['x'] = ($translation['x'] - $viewer_position['x']) * ($viewer_position['z'] / $translation['z']);
				$projection['y'] = ($translation['y'] - $viewer_position['y']) * ($viewer_position['z'] / $translation['z']);
				$point[$x][$y] = $projection;
			}
		}
		imagedestroy($matrix);
		// obrázek captcha
		$captcha = imagecreatetruecolor($captcha_dim['x'], $captcha_dim['y']);
		// antialiasing čar - pro menší zátěž lze vypnout
		if(self::$captcha_anti_alias){
			imageantialias($captcha, true);
		}
		$black = imagecolorexact($captcha, 0, 0, 0);
		$white = imagecolorexact($captcha, 255, 255, 255);
		imagefill($captcha, 0, 0, $white);
		// vykreslení vrstevnic
		for ($x = 1; $x < $matrix_dim['x']; $x++) {
			for ($y = 1; $y < $matrix_dim['y']; $y++) {
				imageline($captcha, -$point[$x - 1][$y - 1]['x'] + $offset['x'], -$point[$x - 1][$y - 1]['y'] + $offset['y'], -$point[$x][$y]['x'] + $offset['x'], -$point[$x][$y]['y'] + $offset['y'], $black);
			}
		}
		imagepng($captcha,self::$img_app_path . 'captcha.png');
		// vystup do souboru ktery se nasledovne echne
		if($javascript){
		echo '<noscript>';
		}
		echo '<img class="captcha-img" src="' . self::$img_path . 'captcha.png" alt="" /><br class="captcha-br" />';
		$this->_('<input type="text" name="captcha" class="captcha-input" />',1,'emptyCaptcha');
		if($javascript){
			echo '</noscript><br class="captcha-br" />';
			echo '<script type="text/javascript">
						document.write(\'<input type="hidden" name="captcha" value="'. substr($captcha_str, 0, 2) .'\' + \''. substr($captcha_str, 2, 2) .'" />\');
				  </script> ';
		}

		// ulozeni hodnoty do session
		//$this->ses['captcha'] = $captcha_str;
		$this->ses_captcha = $captcha_str; // ulozeni primo do session aby se dala hodnota jednoduse testovat ve validatoru
		$this->captcha = false; // zakaze znovuvypsani captchy
	}

	/**
	 * @param int $len string length
	 * @return string rand string
	 */
	private function rand_str($len){
		// generování náhodného textu
		$pool = 'abcdefghijkmnopqrstuvwxyz';
		$captcha = '';
		for ($i=0; $i < $len; $i++) {
			$captcha .= substr($pool, mt_rand(0, strlen($pool) -1), 1);
		}
		return $captcha;
	}

	// Ulozi errory
	private function save_error($emsg,$index=""){
		if(!empty($this->validators->emsg[$emsg])){
			$msg = $this->validators->emsg[$emsg];
		}else{
			$msg = $emsg;
		}
		if($index){
			$this->not_valid_data[$index] = $msg; // uloeeni nevalidniho prvku
		}
        if($this->save_errors && $emsg!='%empty%' && !in_array($msg,$this->errors)
				&& !(!empty($this->no_error_names) && in_array($index,$this->no_error_names))){ // pokud neni v zakazanych
			$this->errors[] = $msg;
        }
    }

    // specificky predelava pole na string.. napr: a[1][2][3]="b" -> a['1[2][3]']="b"
    private function transform2str($data,$prefix="") {
		static $transData;
        foreach($data as $i=>$d){
            if($prefix){
                $i = $prefix."[".$i."]";
            }
            if(is_array($d)){
                $this->transform2str($d,$i);
            }else{
                $transData[$i] = $d;
            }
        }
        if(!$prefix){
            $result = $transData;
            $transData = array();
            return $result;
        }
    }

    // inverse function to transform2str using $_POST
    private function transform2array($data,$post=NULL,$prefix="") {
		if($post===NULL) $post = $_POST;
		foreach($post as $i=>$p){
			if(is_array($p)){
				if(!$prefix) $prefix_for = $i;
				else $prefix_for = $prefix."[$i]";
				$result[$i] = $this->transform2array($data,$p,$prefix_for);
			}else{
				$ii = $prefix ? "[$i]" : $i;
				$result[$i] = @$data[$prefix.$ii]; // for debug remove "@"
			}
		}
        return $result;
    }

    // specialni transformace nazvu pro multiple-select
	private function mult_transform($d,$index) {
		$last_index = "";
		foreach($d as $i_d=>$d_d){
			if(strpos($i_d,$index)!==false){
				$c = 0;
				while(strlen($i_d)>$c){
					if($i_d[$c]!="[" && $i_d[$c]!="]"){
						$last_index .= $i_d[$c];
					}elseif($i_d[$c]=="[")
						$last_index = "";
					$c++;
				}
				$d[$index][$last_index]=$d_d;
			}
		}
		return $d;
	}
	/**
	 * Vypise error zvoleneho name, nebo vytvori tag pro ajaxove vypisovani
	 * @param string $name
	 */
	private function print_under($name){
		echo $decorator = str_replace("%id%", "error_".$name,'<span id="%id%">');
		if(isset($this->not_valid_data[$name]) && $this->not_valid_data[$name]!='%empty%'){ // vypsani error hlasky pod imput
			echo str_replace('%msg%', $this->not_valid_data[$name], self::$errors_decorator);
		}
		echo '</span>';
	}
}
?>

ACC SHELL 2018