<?php 
api::dbtableapidata(function($data){
	$classname = "db_".$data["classname"];
	if (class_exists($classname)) {
		return $classname::{"api_".$data["action"]}($data["data"]);
	}
});
api::install(function($data){
	db_table::install();
});
api::echoJs(function($data){
	ob_start();
	db_table::echoJs();
	$code=ob_get_clean();
});
Route::isJs("app.js",function(){
	db_table::echoJs();
});
app::on("before-run",function(){
	db_table::route();
});
api::saveupdatedataconfigfile(function($data){
	return eval($data["code"]);
});
Route::isCss(function(){
?>
<style>
	.filter{
		display: flex;
		align-items: flex-end;
	}
	.filter > *,.filter .form-group {
		margin: 0;
	}
	.filter > *+*{
		margin-left: 5px;
	}
	.filter-adap{
		display: flex;
		align-items: flex-end;
	}
	.filter-adap > *,.filter-adap .form-group {
		margin: 0;
	}
	.filter-adap > *+*{
		margin-left: 5px;
	}
	@media only screen and (max-width: 480px){
		.filter-adap{
			display: flex;
			align-items: flex-end;
			flex-direction: column;
		}
		.filter-adap > *{
			width: 100%;
		}
		.filter-adap > *,.filter-adap .form-group {
			margin: 0;
		}
		.filter-adap > *+*{
			margin-left: 0px;
			margin-top: 10px;
		}
	}
	form *[type=submit]{
		position: relative;
	}
	form.loading *[type=submit]:before {
	    content: "...";
	    position: absolute;
	    left: 0;
	    right: 0;
	    top: 0;
	    bottom: 0;
	    background: inherit;
	    display: flex;
	    align-items: center;
	    justify-content: center;
	}
</style>
<?php
});
class db_table implements JsonSerializable{
	var $_data = [];
	static $_config = [];
	static $viewList = [];
	function __construct($data = null){
		$this->_data = $data;
	}
	static function getAllCols(){
		$list = [];
		foreach (self::$_colsList as $dblist) {
			foreach ($dblist as $col) {
				$list[] = $col;
			}
		}
		return $list;
	}
	function __call($name,$args){
		if (is_callable($args[0])) {
			ob_start();
			$v = $args[0];
			$v = $v->bindTo($this);
			$v = $v();
			$code=ob_get_clean();
			if (is_null($v)) {
				$this->_data[$name] = $code;
			}else{
				$this->_data[$name] = $v;
			}
		}else{
			$this->_data[$name] = $args[0];
		}
		return $this;
	}
	function __set($name,$value){
		$this->_data[$name] = $value;
	}
	function __get($name){
		return $this->_data[$name];
	}
	static function param($names,$default = null){
		$classname = get_called_class();
		if(is_string($names)) $names = explode(",", $names);
		foreach ($names as $name) {
			if (isset($classname::$_config[$name])) {
				return $classname::$_config[$name];
			}
		}
		return $default;
	}
	var $_index = [];
	var $_unique = [];
	var $_primary = [];
	static function allClass(){
		$children  = array();
		foreach(get_declared_classes() as $class){
		    if(is_subclass_of($class,"db_table")) $children[] = $class;
		}
		return $children;
	}
	static function install(){
		$list = db_table::allClass();
		self::defineAllCols($list);
		foreach ($list as $db_class) {
			(new $db_class())->dbinstall();
		}
	}
	static function route(){
		$list = db_table::allClass();
		self::defineAllCols($list);
		foreach ($list as $db_class) {
			if($route = $db_class::param("route")){
				Route::is($route,function() use($db_class){
					view::is($db_class::param("view"));
					$r = [];
					foreach ($_GET as $key => $value) {
						if (is_string($key)) {
							if (is_numeric($value)) {
								$r[$key] = [
									"hide"  => true,
									"value" => ["id"=>$value]
								];
							}
						}
					}
					if (!empty($r)) {
						$r = "filter='".json_encode($r)."'";
					}else{
						$r = "";
					}
					?>
					<h1><?= $db_class::param("titre_page") ?></h1>
					<hr style="width: 100%;margin-bottom: 0;">
					<<?= $db_class::_getDirectiveName() ?> widget="mdl" <?= $r ?>></<?= $db_class::_getDirectiveName() ?>>
					<?php
				});
			}
		}
	}
	static function echoJs(){
		$list = db_table::allClass();
		self::defineAllCols($list);
		echo "<script>";
		foreach ($list as $db_class) {
			echo (new $db_class())->getJs();
		}
		echo "</script>";
	}
	static function getOrInsert($titre){
		$classname = get_called_class();
		$coltitre = $classname::param("col_titre","titre");
		$tablename = "¤".strtoupper(preg_replace("/^db_/i","", $classname));
		if ($row = db::row("SELECT id,$coltitre as titre from $tablename where $coltitre = :titre",[":titre"=>$titre])) {
			return $row;
		}else{
			$row = [];
			$row["id"] = db::save($tablename,[$coltitre=>$titre]);
			$row["titre"] = $titre;
			$row["reload"] = true;
			return $row;
		}
	}
	static function getCol($name){
		$classname = get_called_class();
		if (is_array(db_table::$_colsList[$classname])) {
			if (isset(db_table::$_colsList[$classname][$name])) {
				return db_table::$_colsList[$classname][$name];
			}
		}
	}
	static function forEachCol($callback){
		$list = self::getColsByOrder("order");
		if(is_callable($callback)){
			foreach ($list as $col) {
				$callback($col);
			}
		}
		return $list;
	}
	static function getColsByOrder($orderby,$DESC_ASC = "ASC"){
		$classname = get_called_class();
		if (is_array(db_table::$_colsList[$classname])) {
			$rep = db_table::$_colsList[$classname];
			usort($rep, function($a,$b) use ($orderby,$DESC_ASC){
				if ($DESC_ASC == "ASC") {
					if ($a->$orderby<$b->$orderby) {
						return -1;
					}elseif ($a->$orderby>$b->$orderby){
						return 1;
					}else{
						return 0;
					}
				}else{
					if ($a->$orderby>$b->$orderby) {
						return -1;
					}elseif ($a->$orderby<$b->$orderby){
						return 1;
					}else{
						return 0;
					}
				}
			});
			return $rep;
		}
		return [];
	}
	static function rmvTag($code,$tag = "script"){
	    $code = trim($code);
	    if(preg_match("/^\s*<".$tag."[^>]*>/i", $code)) $code = preg_replace("/^\s*<".$tag."[^>]*>/i", "", $code);
	    if(preg_match("/<\/".$tag.">\s*$/i", $code)) $code = preg_replace("/<\/".$tag.">\s*$/i", "", $code);
	    return $code;
	}
	static function from_camel_case($input,$sep="-") {
	  return ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', $sep.'$0', $input)), $sep);
	}
	static function _getDirectiveName($html = false){
		$db_class = get_called_class();
		$classname = preg_replace("/^db_/i","",$db_class);
		$mdlname = explode("_",$classname);
		$mdlname = array_map(function($elm){
			return ucfirst($elm);
		}, $mdlname);
		if (!empty($mdlname)) {
			$mdlname[0] = strtolower(substr($mdlname[0],0,1)).substr($mdlname[0],1);
		}
		$mdlname = join("",$mdlname);
		if($html){
			return db_table::from_camel_case($mdlname);
		}
		return $mdlname;
	}
	function getDirectiveName(){
		$db_class = get_class($this);
		$classname = preg_replace("/^db_/i","",$db_class);
		$mdlname = explode("_",$classname);
		$mdlname = array_map(function($elm){
			return ucfirst($elm);
		}, $mdlname);
		if (!empty($mdlname)) {
			$mdlname[0] = strtolower(substr($mdlname[0],0,1)).substr($mdlname[0],1);
		}
		$mdlname = join("",$mdlname);
		return $mdlname;
	}
	static function filter($dbquery,$data){
		$classname = get_called_class();
		$filter = $dbquery->filter($data["filter"]);
		$list = $classname::getColsByOrder("order");
		foreach ($list as $col) {
			$col->onCreateFilter($filter);
		}
		return $filter;
	}
	static function initItem(&$item,$query){
	}
	static function beforeSendData(&$rep,$query){
	}
	static $dataapi = [];
	static function api_loadData($data){
		db_table::$dataapi = $data;
		$classname = get_called_class();
		$rep = $classname::filter($classname::getfilter(),$data)->order("id","DESC")->pagination($data["page"]);
		foreach ($rep["data"] as $key => &$item) {
			$classname::initItem($item,$classname::filter($classname::getfilter(),$data));
		}
		$classname::beforeSendData($rep,$classname::filter($classname::getfilter(),$data));
		return $rep;
	}
	static function api_autocomplete($data){
		if(empty($data["name"])) $data["name"] = $_GET["name"];
		if(empty($data["q"])) $data["q"] = $_GET["q"];
		$colname = $data["name"];
		$classname = preg_replace("/^db_/i","", get_called_class());
		$tablename = "¤".strtoupper($classname);
		$sqls = [];
		$sqls[] = "SELECT $colname as value, 10 as score FROM $tablename where $colname like CONCAT(:q,'%') group by $colname";
		$sqls[] = "SELECT $colname as value, 5 as score FROM $tablename where $colname like CONCAT('%',:q,'%') group by $colname";
		$sqls[] = "SELECT $colname as value, 1 as score FROM $tablename where $colname like :q1 group by $colname";
		$sqls = "SELECT value FROM (SELECT value,max(score) as score from (".join(" union ",$sqls).") t group by value) t1 order by score DESC LIMIT 0,10";
		$rep =  db::table($sqls,[":q"=>trim($data["q"]),":q1"=>"%".str_replace(" ","%",trim($data["q"]))."%"]);
		return $rep;
	}
	static function api_save($item){
		$classname = get_called_class();
		$item = new $classname($item);
		return $item->save();
	}
	static function api_delete($item){
		$classname = get_called_class();
		$item = new $classname($item);
		// journal::add("Tableau ".$classname::$_config["titre"]." : Suppression ligne id : ".$item->id);
		return $item->delete();
	}
	static function api_getdataforselect($data){
		$classname = get_called_class();
		if (!empty($data["join"])) {
			$data["join"] .=",id,".$classname::$_config["col_titre"]." as titre";
		}else{
			$data["join"] = "id,".$classname::$_config["col_titre"]." as titre";
		}
		$filter = $classname::getfilter();
		$nbr = $filter->count();
		if (!empty($data["q"])) {
			$filter->where($classname::$_config["col_titre"]." like :q",[":q"=>"%".preg_replace("/\s+/i","%",$data["q"])."%"]);
		}
		if (is_array($data["filter"]) && !empty($data["filter"])) {
			foreach($data["filter"] as $k => $val){
				$filter->where("$val");
			}
		}

		$id = $data["id"];
		$col = "0 as n";
		if (is_numeric($id)) {
			$col = "IF(id = $id,1,0) as n";
		}

		return [
			"data" => $filter->select($data["join"].",$col")->orderBy("n","DESC")->limit("0,30")->all(),
			"useSelectTag" => $nbr<30?true:false,
		];
	}
    public function jsonSerialize(){
        return $this->_data;
    }
	function save(){
		$classnamethis = get_class($this);
		$list = $classnamethis::getColsByOrder("order");
		foreach ($list as $col) {
			$col->beforeSave($this);
		}
		$control = $this->validationfn();
		if (!is_array($control)) {
			$etat = $control;
		}else{
			$etat = array_reduce($control,function($rep,$col){ return $rep && $col["etat"];},true);
		}
		if ($etat) {
			$isupdate = is_numeric($this->_data["id"]);
			$this->_data["isUpdate"] = $isupdate;
			$this->_data["isInsert"] = !$isupdate;
			if($this->_data["isInsert"]){
				$this->_data["iduserapp"] = $_SESSION["user"]["id"];
			}
			if ($isupdate) {
				db::save($this->getTableName(),$this->_data);
			}else{
				$this->_data["id"] = db::save($this->getTableName(),$this->_data);
			}
			client::notification("Enregistré","success");
			if ($isupdate) {
				// journal::add("Tableau ".$classnamethis::$_config["titre"]." : Mise a jour ligne id : ".$this->_data["id"]);
				foreach (db_table::$_colsList as $classn) {
					foreach ($classn as $col) {
						$col->onUpdateItem($classnamethis,$this);
					}
				}
			}else{
				// journal::add("Tableau ".$classnamethis::$_config["titre"]." : Insertion ligne id : ".$this->_data["id"]);
			}
			foreach ($list as $col) {
				$col->afterSave($this);
			}
			Event::run($classnamethis."::save",$this);
		}
		return [
			"ok"      => $etat,
			"item"    => $this,
			"control" => $control
		];
	}
	function validation(){
		return [];
	}
	function validationfn(){
		$rep = $this->validation();
		$list = get_class($this)::getColsByOrder("order");
		foreach ($list as $col) {
			$r = $col->validation($this);
			if(is_array($r) && !empty($r)){
				$r = [$col->name => $r];
				$rep += $r;
			}
		}
		return $rep;
	}
	function delete(){
		if (is_numeric($this->_data["id"])) {
			$classnamethis = get_class($this);
			if (db::query("DELETE FROM ".$this->getTableName()." where id=".$this->_data["id"])) {
				Event::run($classnamethis."::delete",$this);
				client::notification("Supprimé","success");
				return true;
			}
		}
		return false;
	}
	function getTableName(){
		$classname = preg_replace("/^db_/i","", get_class($this));
		return "¤".strtoupper($classname);
	}
	function jsForm(){
	}
	static $BtnShowViewList = [];
	static function addBtnShowView($name,$titre,$callback_or_string,$attrsDialog="",$script="",$attrsBtn = ""){
		$classname = get_called_class();
		if (is_callable($callback_or_string)) {
			ob_start();
			$callback_or_string();
			$callback_or_string=ob_get_clean();
		}
		if(is_callable($script)){
			ob_start();
			$script();
			$script=ob_get_clean();
		}
		if(empty(self::$BtnShowViewList[$classname])) self::$BtnShowViewList[$classname] = [];
		self::$BtnShowViewList[$classname][$name] = [
			"name"     => $name,
			"titre"    => $titre,
			"tpl"      => $callback_or_string,
			"attrs"    => $attrsDialog,
			"script"   => $script,
			"attrsBtn" => $attrsBtn,
		];
	}
	function tplGlobalAction($i){
	}
	function tplAction($i){
	}
	static function createView($name,$titre,$callback = null) {
		Route::isJs($callback);	
		self::$viewList[$name] = [
			"titre" => $titre
		];
	}
	function tpl(){
		$classname = get_class($this);
		$mdlname = db_table::from_camel_case($this->getDirectiveName());
		$list = $classname::getColsByOrder("orderTable");
		ob_start();
		?>
			<div>
				<<?= $mdlname ?>-api-data ng-model="mdl" filter-api="filterApi" default="default" control="filter" rules="ctrl.can" autoload="true" titre="<?= ucfirst($mdlname) ?>" dialogs="ctrl.dialogs" init-item="initItem(item)">				
				<<?= $mdlname ?>-filter style="flex:1" filter="mdl.filter" control="filter">
					<? $this->tplGlobalAction(0) ?>
					<button class="btn btn-default" ng-click="mdl.loadData(mdl.page)"> <span class="glyphicon glyphicon-refresh"></span> Actualiser</button>
					<? $this->tplGlobalAction(1) ?>
					<?php if (empty(get_class($this)::$_config["hide_add_btn"])): ?>
					<button class="btn btn-primary" ng-disabled="!can.add" ng-click="mdl.add()"> <span class="glyphicon glyphicon-plus"></span> Ajouter</button>	
					<?php endif ?>
					
					<pre style="display:none">{{can | json}}</pre>
					<? $this->tplGlobalAction(2) ?>
				</<?= $mdlname ?>-filter>
				<div style="background: #fff;min-height: 300px;border: 1px solid #ddd;margin-bottom: 15px;overflow-x: auto;">
					<md-progress-linear md-mode="{{mdl.loading?'indeterminate':'determinate'}}" value="0"></md-progress-linear>
					<table class="table table-striped" style="margin: 0;">
						<tr>
							<?php if (get_class($this)::$_config["check"]&&($_SESSION["user"]["type"] != "Parent")): ?>
								<th style="width: 1px;">
									<md-checkbox ng-model="mdl.checkedall" ng-change="checkedAllFn()" style="margin:0px;max-width:20px" aria-label="string"></md-checkbox>
								</th>
							<?php endif ?>
							<?php if (get_class($this)::$_config["showid"]): ?>
								<th style="width: 1px;">
									ID
								</th>
							<?php endif ?>
							<?php 
							foreach ($list as $col) {
								if ($col->inTable) {
									$col->tplTableTh();
								}
							}
							?>
							<th style="width: 1px;white-space: nowrap;text-align: right;"></th>
						</tr>
						<tr ng-repeat="item in mdl.data" style="position: relative;" ng-class="<?= empty(get_class($this)::$_config["ngClassTr"]) ? "{'success':item.\$checked}" : get_class($this)::$_config["ngClassTr"]?>">
							<?php if (get_class($this)::$_config["check"]&&($_SESSION["user"]["type"] != "Parent")): ?>
								<td style="width: 1px;">
									<md-checkbox ng-model="item.$checked" ng-change="item.$check()" aria-label="checked" style="margin: 0"></md-checkbox>
								</td>
							<?php endif ?>
							<?php if (get_class($this)::$_config["showid"]): ?>
								<td style="width: 1px;text-align: right;">
									{{item.id}}
								</td>
							<?php endif ?>
							<?php 
							foreach ($list as $col) {
								if ($col->inTable) {
									$col->tplTableTd();
								}
							}
							?>
							<td style="width: 1px;white-space: nowrap;">
								<div class="tabletdactions" <?= get_class($this)::$_config["TableTDActionsAttrs"] ?>>
								<?php 
									$this->tplAction(1);
									if (!empty(self::$BtnShowViewList[$classname])) {
										foreach (self::$BtnShowViewList[$classname] as $key => $value) {
											?>
											<button class="btn btn-xs" <?= $value["attrsBtn"] ?> ng-click="ctrl.item=item;item.$dialog.<?= $key ?>();"><?= $value["titre"] ?></button>
											<?php
										}
									}
									$this->tplAction(2);
									$list = db_table::getAllCols();
									foreach ($list as $col) {
										$col->tplActions(get_class($this));
									}
									$this->tplAction(3);
								?>
								<?php if (empty(get_class($this)::$_config["hideitemaction"])): ?>
									<button ng-disabled="can.update==0 || (can.update == 1 && item.iduserapp != <?= $_SESSION["user"]["id"] ?>)" ng-click="item.$edit()" class="btn btn-xs"><span class="glyphicon glyphicon-pencil"></span</button>
									<button ng-disabled="can.delete==0 || (can.delete == 1 && item.iduserapp != <?= $_SESSION["user"]["id"] ?>)" ng-click="item.$delete()" class="btn btn-xs"><span class="glyphicon glyphicon-trash"></span></button>
								<?php endif ?>
								</div>
							</td>
						</tr>
					</table>
					<?php 
						foreach ($list as $col) {
							$col->tplTable(get_class($this));
						}
						if (!empty(self::$BtnShowViewList[$classname])) {
							foreach (self::$BtnShowViewList[$classname] as $key => $value) {
								?>
								<dialog ng-model="ctrl.dialogs.dialogBtnShowView<?= $key ?>" <?= $value["attrs"] ?>>
									<?= $value["tpl"] ?>
								</dialog>
								<?php
							}
						}
					?>
					<style>
						.tabletdactions{
							display: flex;
							align-items: center;
							justify-content: flex-end;
						}
						.tabletdactions > * + *{
							margin-left: 10px;
						}
					</style>
				</div>
				</<?= $mdlname ?>-api-data>
				<div style="float: right;display: flex;">
					<?php $this->tplGlobalBtns() ?>
					<button style="margin:0 5px" class="btn" type="button">Page : {{mdl.page}} / {{mdl.nbrPage}}</button>
					<button  class="btn" type="button">Total : {{mdl.total}} <span class="glyphicon glyphicon-floppy-disk"></span></button>
				</div>
				<div style="clear:both;"></div>
				<?php $this->tplGlobalBottom() ?>
			</div>
		<?php
		return preg_replace("/\t|\n|\r/i", "", ob_get_clean());
	}
	function tplGlobalBottom(){
	}
	function tplGlobalBtns(){
	}
	function tplForm(){
		$classname = get_class($this);
		$list = $classname::getColsByOrder("order");
		$tabs = [];
		foreach ($list as $col) {
			$v = $col->inTab;
			if (!empty($v)) {
				$tabs[$v] = $v;
			}
		}
		?>
		<div class="row">
<?php 
	foreach ($list as $col) {
		$v = $col->inTab;
		if (empty($v)) {
			if ($col->sep) {
				?> </div><div class="row"> <?php
			}
			$hidden = $col->hidden;
			if ($col->form && $hidden !== true) {
				?>
				<col-<?= str_replace("_","",$col->name) ?> item="item" control="control" control-filter="controlFilter" default="default" class="col-xs-<?= $col->col ?>" onitemchange="onitemchange.<?=str_replace("_","",$col->name) ?>" ctrl-form="ctrl"></col-<?=str_replace("_","",$col->name) ?>>
				<?php
			}
		}
	}
?>
<?php if (!empty($tabs)): ?>
		<div class="col-xs-12">
			    <md-tabs md-dynamic-height md-border-bottom>
<?php foreach ($tabs as $tab) { ?>
			    	<md-tab label="<?= $tab ?>">
			    		<md-content class="md-padding">
			    			<div class="row">
<?php 
			foreach ($list as $col) {
				$v = $col->inTab;
				if ($v == $tab) {
					if ($col->sep) {
						?> </div><div class="row"> <?php
					}
					$hidden = $col->hidden;
					if ($col->form && $hidden !== true) {
						?>
						<col-<?= str_replace("_","",$col->name) ?> item="item" control="control" control-filter="controlFilter" default="default" class="col-xs-<?= $col->col ?>" onitemchange="onitemchange.<?= str_replace("_","",$col->name) ?>" ctrl-form="ctrl"></col-<?= str_replace("_","",$col->name) ?>>
						<?php
					}
				}
			}
?>
							</div>
			    		</md-content>
			    	</md-tab>
<?php } ?>
 
				</md-tabs>
			</div>
<?php endif ?>
		</div>
<?php
	}
	function _tplForm(){
		$mdlnamecamel = db_table::from_camel_case($this->getDirectiveName());
		ob_start();
		$this->tplForm();
		return preg_replace("/\t|\n|\r/i", "",preg_replace("/<\/col-/","</$mdlnamecamel-",preg_replace("/<col-/","<$mdlnamecamel-",ob_get_clean())));
	}
	function customFilter(){
		
	}
	function tplFilter(){
		$classname = get_class($this);
		$list = $classname::getColsByOrder("orderFilter");
		?>
		<div class="filter-<?= $classname ?>">
			<?php 
			foreach ($list as $col) {
				if ($col->filter || $col->inFilter) {
				?>
				<col-<?= str_replace("_","",$col->name) ?>-filter filter="filter" control="control"></col-<?= str_replace("_","",$col->name) ?>-filter>
				<?php
				}
			}
			$this->customFilter();
			?>
			<span style="flex:1"></span>
			<ng-transclude style="display: flex;flex-wrap: nowrap;"></ng-transclude>
			<style type="text/css">
				.filter-<?= $classname ?>{
					display: flex;
					align-items: flex-end;
					margin: 15px 0; 
					flex-wrap: nowrap;
				}
				.filter-<?= $classname ?> > *{
					margin: 0;
				}
				.filter-<?= $classname ?> > *+*{
					margin-left: 5px;
				}
				.filter-<?= $classname ?> > ng-transclude > *{
					margin-left: 5px;
				}
			</style>
		</div>
		<?php
	}
	function _tplFilter(){
		$mdlnamecamel = db_table::from_camel_case($this->getDirectiveName());
		ob_start();
		$this->tplFilter();
		return preg_replace("/\t|\n|\r/i", "",preg_replace("/<\/col-/","</$mdlnamecamel-",preg_replace("/<col-/","<$mdlnamecamel-",ob_get_clean())));
	}
	function tplApiData(){
		$mdlname = db_table::from_camel_case($this->getDirectiveName());
		ob_start();
		?>
		<div>
			<div style="position: relative;">
				<div style="position: absolute;top: 0;left: 0;right: 0;bottom: 0;display: flex;align-items: center;justify-content: center;background: rgba(255,255,255,0.4);" ng-show="loading">
					<md-progress-circular md-mode="indeterminate"></md-progress-circular>
				</div>
				<div>
					<div ng-transclude></div>
				</div>
				<div style="float: left;" class="paginationcontent">
					<style>
						.paginationcontent  nav  ul {
							margin:0 !important; 
						}
					</style>
					<pagination ng-hide="ngModel.loading" length="ngModel.nbrPage" page="ngModel.page" ng-page="ngModel.loadData(page)"></pagination>
				</div>
			</div>
			<div>
				<dialog ng-model="dialog" titre="<?= get_class($this)::$_config["titre"] ?>" btn-fermer="true" btn-valider="true" ng-valider="valider(fermer)" size="<?= get_class($this)::$_config["dialog_size"] ?>">
					<div style="position: absolute;z-index: 100;top: 0;right: 0;left: 0;bottom: 0;display:flex;background: rgba(255,255,255,0.5);align-items: center;justify-content: center;" ng-if="dialog.loading || !dialog.visible">
						<md-progress-circular md-mode="indeterminate" md-diameter="48"></md-progress-circular>
					</div>
					<div ng-if="!dialog.visible" style="min-height: 250px;"></div>
					<div ng-if="dialog.visible">
						<<?= $mdlname ?>-form item="ngModel.item" control-filter="controlFilter" control="ngModel.control" onitemchange="itemChangeList" ></<?= $mdlname ?>-form>
					</div>
				</dialog>
			</div>
		</div>
		<?php
		return preg_replace("/\t|\n|\r/i", "", ob_get_clean());
	}
	function tplSelect(){
		$db_class = get_class($this);
		$classname = preg_replace("/^db_/i","",$db_class);
		$mdlname = $this->getDirectiveName();
		ob_start();
		?>
	<div>
		<div style="display: flex;margin-bottom: 5px;align-items: center;" ng-show="label">
			<button class="btn btn-xs btn-default" ng-hide="hideadd" ng-click="linknew<?= $mdlname ?>.add()" style="margin-right: 10px;zoom:0.8" ng-hide="_.ctrl.hide">
				<span class="glyphicon glyphicon-plus"></span> 
			</button>
			<label style="display:block;flex:1;margin:0;" class="control-label">{{label}}</label>
			<div style="display: none;">
			<<?= $mdlname ?>-api-data ng-model="linknew<?= $mdlname ?>" on-add="ngModel = elm;ngModel.titre = elm.<?= ("db_".$mdlname)::param("col_titre","titre") ?>;" autoload="false" is-filter="isFilter"></<?= $mdlname ?>-api-data>
			</div>
		</div>
		<div style="width: 100%;">
			<style type="text/css">
				.ui-select-bootstrap>.ui-select-choices, .ui-select-bootstrap>.ui-select-no-choice {
					position: absolute !important;
				}
				.ui-select-toggle{
					border: 1px solid #ccc;
				}
			</style>
			<div ng-if="useSelectTag">
				<select class="form-control avs" ng-model="ctrlSelect.ngModel" ng-options="item as item.titre for item in data track by item.id">
				</select>
			</div>
			<div ng-if="!useSelectTag" style="min-width: 150px;">
				<ui-select ng-model="ctrlSelect.ngModel" on-select="selectedelm($item, $model)" theme="bootstrap" style="display: block;width: 100%;">
				    <ui-select-match placeholder="{{placeholder}}">{{ctrlSelect.ngModel.titre}}</ui-select-match>
				    <ui-select-choices style="min-height: 175px;position: relative;" refresh="loadData($select)" refresh-delay="100" repeat="item in data | filter: $select.search" ui-disable-choice="item.loading">
				      <div>
				      	<div ng-hide="item.loading" ng-bind-html="item.titre | highlight: $select.search"></div>
				      	<div ng-show="item.loading" style="padding: 5px 0;position: absolute;top: 0;left: 0;right: 0;bottom: 0;display: flex;align-items: center;justify-content: center;">
				      		<md-progress-circular md-mode="indeterminate"></md-progress-circular>
				      	</div>
				      </div>
				    </ui-select-choices>
				</ui-select>
			</div>
		</div>
	</div>
 
		<?php
		return preg_replace("/\t|\n|\r/i", "", ob_get_clean());
	}
	function getJs(){
		$self = $this;
		$db_class = get_class($this);
		$classname = preg_replace("/^db_/i","",$db_class);
		$mdlname = $this->getDirectiveName();
		$rep = [];
		ob_start();
		?>
		<script>
			window.dbTable = window.dbTable || {};
			window.dbTable.apicachelist = [];
			window.dbTable.apicache = function(apiname,data,callback){
				var ins = window.dbTable.apicachelist.find(function(ins){
					return (ins.api == apiname && ins.data.data.q == data.data.q);
				});
				if (ins) {
					ins.callback.push(callback);
				}else{
					var id = uniqid()+uniqid()+Math.random();
					window.dbTable.apicachelist.push({
						id : id,
						api : apiname,
						data : data,
						callback: [callback]
					});
					api(apiname,data,function(rep){
						for (var i = 0; i < window.dbTable.apicachelist.length; i++) {
							if (window.dbTable.apicachelist[i].id == id) {
								window.dbTable.apicachelist[i].callback.forEach(function(cb){
									var repcb = angular.copy(rep);
									if(cb) cb(repcb);
								});
								window.dbTable.apicachelist.splice(i,1);
							}
						}
					});
				}
			}
			app.directive('<?= $mdlname ?>Select', [function () {
				return {
					restrict: 'E',
					template : <?= json_encode($this->tplSelect()) ?>,
					scope : {ngModel:"=",titre : "@?",join:"@?",ngChange:"&?",empty:"@?",default:"=?",onitemchange:"=?",label:"@?",hideadd:"=?", filter : "=?", isFilter:"=?"},
					replace : true,
					controller : function($scope){
						$scope.ctrlSelect = $scope;
						$scope.loading = false;
						$scope.activeEmpty = false;
						$scope.useSelectTag = true;
						$scope.api = function(name,data,callback){
							$scope.loading = true;
							window.dbTable.apicache("dbtableapidata&classname=<?= $classname ?>&action="+name,{
								classname:"<?= $classname ?>",
								action:name.split("&")[0],
								data:data
							},function(rep){
								$scope.loading = false;
								if(callback) callback(rep);
								$scope.$apply();
							});
						}
						$scope.selectedelm = function(item){
							$scope.ngModel = angular.copy(item);
						}
						$scope.data = [];
						$scope.lastq = "";
						$scope.loadData = function($select){
							if($select){
								var s = $select.search || "";
								if(s == $scope.lastq) return;
								$scope.lastq = s;
							}
							$scope.api("getdataforselect",{join:$scope.join,q:$select?$select.search:'',filter : $scope.filter || {}},function(rep){
								rep = rep || {};
								if ($scope.ngModel && $scope.ngModel.id) {
									if(!rep.data.find(function(elm){ return elm.id == $scope.ngModel.id;})){
										rep.data.push(angular.copy($scope.ngModel));
									}
								}
								$scope.data = rep.data || [];
								$scope.useSelectTag = !!rep.useSelectTag;
								$scope.actDefault();
								$scope.injectEmptyValue();
							});
						}
						$scope.$root.$on('reload-select',function(event,args){
							if (args.name == "<?= $classname ?>") {
								if(!$scope.data.find(function(elm){ return args && args.id && elm.id == args.id;})){
									$scope.loadData();
								}
							}
						});
						$scope.injectEmptyValue = function(){
							for (var i = 0; i < $scope.data.length; i++) {
								if($scope.data[i].empty){
									$scope.data.splice(i,1);
								}
							}
							if ($scope.activeEmpty) {
								$scope.data.unshift({empty:true,titre : $scope.empty});
							}
						};
						$scope.$watch("activeEmpty",function(){
							$scope.injectEmptyValue();
						});
						$scope.injectEmptyValue();
						// $scope.loadData();
						window.dbTable = window.dbTable || {};
						window.dbTable.<?= $mdlname ?> = window.dbTable.<?= $mdlname ?> || {};
						window.dbTable.<?= $mdlname ?>.listSelect = window.dbTable.<?= $mdlname ?>.listSelect || [];
						window.dbTable.<?= $mdlname ?>.listSelect.push($scope);
						window.dbTable_<?= $mdlname ?>_loadData = function(){
							for (var i = 0; i < window.dbTable.<?= $mdlname ?>.listSelect.length; i++) {
								window.dbTable.<?= $mdlname ?>.listSelect[i].loadData();
							}
						}
						$scope.defaultValue = null;
						$scope.actDefault = function(aa){
							$scope.defaultValue = null;
							if ($scope.default && $scope.default.id) {
								var found = false;
								$scope.data.forEach(function(elm){
									if(elm.id+"" == $scope.default.id+""){
										found = true;
										$scope.defaultValue = angular.copy(elm);
										$scope.defaultValue.id = $scope.defaultValue.id*1;
									}
								});
								if (!found && !aa) {
									$scope.api("getdataforselect&nocache="+$scope.default.id,{
										join:$scope.join,
										q:"",
										filter : $scope.filter || {}, 
										id:$scope.default.id
									},function(rep){
										rep = rep || {};
										if ($scope.ngModel && $scope.ngModel.id) {
											if(!rep.data.find(function(elm){ return elm.id == $scope.ngModel.id;})){
												rep.data.push(angular.copy($scope.ngModel));
											}
										}
										$scope.data = rep.data || [];
										$scope.useSelectTag = !!rep.useSelectTag;
										$scope.actDefault(true);
										$scope.injectEmptyValue();
									});
								}
							}
							if($scope.onitemchange) $scope.onitemchange();
						}
						$scope.$watch("default",function(){
							$scope.actDefault();
						});
						$scope.$watch("ngModel",function(){
							if ($scope.ngModel) {
								if ($scope.ngModel.reload && $scope.useSelectTag) {
									$scope.loadData();
								}
							}
							$scope.actDefault();
						});
						$scope.onitemchange = function(){
							if ($scope.defaultValue && (!$scope.ngModel || !$scope.ngModel.id)) {
								if(!$scope.ngModel) $scope.ngModel = {};
								for(var x in $scope.defaultValue){
									$scope.ngModel[x] = $scope.defaultValue[x];
								}
							}
						};
					},
					link: function ($scope, iElement, iAttrs) {
						var oldvalue = null;
						$scope.activeEmpty = "empty" in iAttrs;
						$scope.$watch("ngModel",function(){
							if($scope.ngModel && $scope.ngChange){
								if (!oldvalue){
									$scope.ngChange({value:$scope.ngModel,item:$scope.ngModel});
								}else if (oldvalue.id != $scope.ngModel.id) {
									$scope.ngChange({value:$scope.ngModel,item:$scope.ngModel});
								}
							}else if(oldvalue && $scope.ngChange && !$scope.ngModel){
								$scope.ngChange({value:$scope.ngModel,item:$scope.ngModel});
							}
							oldvalue = $scope.ngModel;
						});
						$scope.loadData();
					}
				};
			}]);
			app.directive("<?= $mdlname ?>ApiData",function(){
				return {
					restrict : "E",
					transclude : true,
					template : <?= json_encode($this->tplApiData()) ?>,
					scope : {ngModel:"=",controlFilter:"=?control",filter:"=?",autoload:"=?",titre:"@",initItem:"&?",default:"=?", onAdd:"&?",initFilter:"&?",dialogs:"=?",filterApi:"=?",rules : "=?",isFilter : "=?"},
					controller : function($scope){
						$scope.ngModel = $scope;
						$scope.dialogs = $scope.dialogs || {};
						$scope.loading = false;
						$scope.control = {};
						$scope.item = {};
						$scope.data = [];
						$scope.can = {
							add : 2,
							update : 2,
							delete : 2,
							show : 2
						};
						$scope.$watch("rules", function(){
							if($scope.rules === true){
								$scope.can = {
									add : 2,
									update : 2,
									delete : 2,
									show : 2
								};
							}else if($scope.rules){
								var dd = ["add","update","delete","show"];
								for (var i = 0; i < dd.length; i++) {
									var k = dd[i];
									$scope.can[k] = $scope.rules[k] * 1;
								}
							}
						},true);
						$scope.$watch("filter",function(){
							if ($scope.filter) {
								if (typeof $scope.filter == "object") {
									$scope.filter.loadData = $scope.ngModel.loadData;
								}
							}
						});
						$scope.valider = function(fermer){
							$scope.dialog.loading = true;
							$scope.item.$loading = true;
							var item = $scope.itemOriginal;
							for(var x in $scope.item){
								if(x != "id"){
									item[x] = $scope.item[x];
								}else if(item.id){
									$scope.item.id = item.id;
								}
							}
							var reloaddata = !item.id || <?= isset(get_class($this)::$_config["reloadOnSave"]) ? ((bool)get_class($this)::$_config["reloadOnSave"] ? "true":"false"):"false" ?>;
							$scope.api("save",$scope.item,function(rep){
								$scope.dialog.loading = false;
								if(rep && rep.item){
									for(var x in rep.item){
										item[x] = rep.item[x];
									}
									if($scope.onAdd) $scope.onAdd({elm:item});
									if(reloaddata||true){
										$scope.loadData();
									}
								}
								if (rep && rep.control) {
									$scope.control = rep.control;
								}
								if (rep && rep.ok) {
									if(window.dbTable_<?= $mdlname ?>_loadData) window.dbTable_<?= $mdlname ?>_loadData();
									if(fermer) fermer();
								}else{
									$scope.dialog.loading = false;
									$scope.dialog.show();
								}
								delete item.$loading;
							});
						}
						$scope.itemChangeList = {};
						$scope.itemChangeed = function(){
							setTimeout(function(){
								for(var x in $scope.itemChangeList){
									if(typeof($scope.itemChangeList[x]) == "function"){
										$scope.itemChangeList[x]();
									}
								}
								$scope.$apply();
							},200);
						};
						$scope.add = function(newItem){
							$scope.control = {};
							$scope.itemOriginal = {};
							$scope.item = newItem || {};
							$scope._initItem($scope.item);
							$scope.dialog.loading = false;
							$scope.dialog.show();
							$scope.itemChangeed();
							$scope.$root.$broadcast("open-dialog",{name:"<?= $mdlname ?>",item : $scope.item});
							$scope.$root.$broadcast("open-dialog-<?= $mdlname ?>",{name:"<?= $mdlname ?>",item : $scope.item});
							$scope.$root.$broadcast("open-dialog-add",{name:"<?= $mdlname ?>",item : $scope.item});
							$scope.$root.$broadcast("open-dialog-<?= $mdlname ?>-add",{name:"<?= $mdlname ?>",item : $scope.item});
						}
						$scope._initItem = function(item){
							item.$save = function(callback){
								item.$loading = true;
								$scope.api("save",item,function(rep){
									delete item.$loading;
									if(callback) callback();
								});
							}
							item.$edit = function(){
								$scope.control = {};
								$scope.itemOriginal = item;
								$scope.item = angular.copy(item);
								$scope.dialog.loading = false;
								$scope.dialog.show();
								$scope.itemChangeed();
								$scope.$root.$broadcast("open-dialog",{name:"<?= $mdlname ?>",item : item});
								$scope.$root.$broadcast("open-dialog-<?= $mdlname ?>",{name:"<?= $mdlname ?>",item : item});
								$scope.$root.$broadcast("open-dialog-edit",{name:"<?= $mdlname ?>",item : item});
								$scope.$root.$broadcast("open-dialog-<?= $mdlname ?>-edit",{name:"<?= $mdlname ?>",item : item});
							}
							item.$dupliquer = function(obj){
								var newItem = angular.copy(item);
								delete newItem.id;
								if(obj){
									for(var x in obj){
										newItem[x] = obj[x];
									}
								}
								$scope.add(newItem);
							}
							item.$dialog = {};
<?php if (!empty(self::$BtnShowViewList[$db_class])) { ?>
<?php foreach (self::$BtnShowViewList[$db_class] as $key => $value) { ?>
							item.$dialog.<?= $key ?> = function(){
								$scope.itemOriginal = item;
								$scope.item = angular.copy(item);
								$scope.dialogs.ctrl.item = item;
								$scope.dialogs.dialogBtnShowView<?= $key ?>.show();
								$scope.itemChangeed();
								$scope.$root.$broadcast("open-dialog",{name:"<?= $key ?>",item : item});
								$scope.$root.$broadcast("open-dialog-<?= $key ?>",{name:"<?= $key ?>",item : item});
							}
<?php } ?>
<?php } ?>
	
							item.$delete = function(force){
								if(!force && !confirm("Voulez vous vraiment supprimer cet enregistrement?")) return;
								$scope.api("delete",item,function(rep){
									if (rep) {
										$scope.loadData();
										if(window.dbTable_<?= $mdlname ?>_loadData) window.dbTable_<?= $mdlname ?>_loadData();
									}
								});
							}
							if($scope.initItem) $scope.initItem({item:item});
						}
						$scope.api = function(name,data,callback){
							data.filterApi = $scope.filterApi;
							$scope.loading = true;
							api("dbtableapidata&classname=<?= $classname ?>&action="+name,{
								classname:"<?= $classname ?>",
								action:name,
								rules : $scope.can,
								data:data
							},function(rep){
								$scope.loading = false;
								if(callback) callback(rep);
								$scope.$apply();
							});
						}
						$scope.loadData = function(page){
							if(!page) page = 1;
							$scope.data = [];
							$scope.api("loadData",{
								filter:$scope.filter,
								page : page
							},function(rep){
								$scope.data = [];
								if (rep && typeof rep == "object") {
									for(var x in rep){
										$scope[x] = rep[x];
									}
								}
								for (var i = 0; i < $scope.data.length; i++) {
									$scope._initItem($scope.data[i]);
								}
							});
						}
						$scope.filter = $scope.filter || {};
						$scope.controlFilter = $scope.controlFilter || {};
						$scope.filter.loadData = $scope.ngModel.loadData;
						$scope.controlFilter.loadData = $scope.ngModel.loadData;
						if($scope.initFilter) $scope.initFilter({$filter:$scope.filter,$mdl:$scope,$controlFilter:$scope.controlFilter});
						$scope.$watch("autoload",function(){
							if($scope.autoload){
								$scope.loadData();
							}
						});
					}
				}
			});
			app.directive("<?= $mdlname ?>",function(){
				return {
					restrict : "E",
					template : <?= json_encode($this->tpl()) ?>,
					scope : {filter:"=?",default:"=?",filterApi:"=?",listChecked:"=?",rules:"=?"},
					controller : function($scope){
						$scope.ctrl = $scope;
						$scope.item = {}; 
						$scope.dialogs = {};
						$scope.dialogs.ctrl = $scope;
						$scope.listChecked = $scope.listChecked || [];
						$scope.can = {
							add : 2,
							update : 2,
							delete : 2,
							show : 2
						};
						$scope.$watch("rules", function(){
							if($scope.rules === true){
								$scope.can = {
									add : 2,
									update : 2,
									delete : 2,
									show : 2
								};
							}else if($scope.rules){
								var dd = ["add","update","delete","show"];
								for (var i = 0; i < dd.length; i++) {
									var k = dd[i];
									$scope.can[k] = $scope.rules[k] * 1;
								}
							}
						},true);
						$scope.checkedAllFn = function() {
						setTimeout(function() {
							$scope.mdl.data
							.map(elm => { 
								elm.$checked = !!$scope.mdl.checkedall; 
								if (elm.$checked) {
									if (!$scope.listChecked.find(elm1 => elm1.id == elm.id)) {
										$scope.listChecked.push(elm);
									}
								} else {
									$scope.listChecked = $scope.listChecked.filter(elm1 => elm1.id != elm.id);
								}
							});
							$scope.$apply();
							}, 100);
						}
						$scope.initItem = function(item){
							$scope.ctrlForm = $scope;
							if($scope.listChecked.find(function(elm){ return elm.id == item.id; })){
								item.$checked = true;
							}else{
								item.$checked = false;
							}
							item.$check = function(){
								if(item.$checked){
									if(!$scope.listChecked.find(function(elm){ return elm.id == item.id;})){
										$scope.listChecked.push(angular.copy(item));
									}
								}else{
									for (var i = 0; i < $scope.listChecked.length; i++) {
										if($scope.listChecked[i].id == item.id){
											$scope.listChecked.splice(i,1);
										}
									}
								}
							};
<?php $classname = get_class($this); $list = $classname::getColsByOrder("order"); $tabs = []; foreach ($list as $col) {ob_start(); $col->initItem(); echo self::rmvTag(ob_get_clean());} ?>
						}
						$scope.location = function(url){
							window.location = url;
						}
						<?php ob_start(); $this->jsForm(); echo self::rmvTag(ob_get_clean()); ?>
<?php
						if (!empty(self::$BtnShowViewList[$db_class])) {
							foreach (self::$BtnShowViewList[$db_class] as $key => $value) {
								echo self::rmvTag($value["script"]);
							}
						}
?>
	
					}
				}
			});
			app.directive('<?= $mdlname ?>Form', [function () {
				return {
					restrict: 'E',
					template : <?= json_encode($this->_tplForm()) ?>,
					scope : {item : "=?",control:"=?",controlFilter:"=?",default:"=?",onitemchange:"=?",},
					controller : function($scope){
						$scope.ctrl = $scope;
						<?php ob_start(); $this->jsForm(); echo self::rmvTag(ob_get_clean()); ?>
					},
					link: function (scope, iElement, iAttrs) {
						
					}
				};
			}]);
			app.directive('<?= $mdlname ?>Filter', [function () {
				return {
					restrict: 'E',
					template : <?= json_encode($this->_tplFilter()) ?>,
					scope : {filter : "=?",control:"=?"},
					transclude:true,
					controller : function($scope){
					},
					link: function (scope, iElement, iAttrs) {
						
					}
				};
			}]);
		</script>
		<?php
		$rep[] = self::rmvTag(ob_get_clean());
		if(is_array(db_table::$_colsList[$db_class]))
		foreach (db_table::$_colsList[$db_class] as $col) {
			ob_start();
			$col->echoJs($this);
			$rep[] = self::rmvTag(ob_get_clean());
		}
		if(is_array(db_table::$listview[$db_class]))
		foreach (db_table::$listview[$db_class] as $view) {
			ob_start();
			$fn = $view["callback"];
			if(is_callable($fn)){
				$fn = $fn->bindTo($this);
				$fn();
			}else{
				echo $fn;
			}
			$rep[] = self::rmvTag(ob_get_clean());
		}
		return join("\n",$rep);		
	}
	static function onDBInstall($table){
	}
	function dbinstall(){
		$self = $this;
		$db_class = get_class($this);
		$db_table = preg_replace("/^db_/i","¤",$db_class);
		// db::query("DROP TABLE IF EXISTS $db_table");
		db::create(strtoupper($db_table),function($table) use ($db_class,$self) {
			$table->int("iduserapp");
			foreach (db_table::$_colsList[$db_class] as $col) {
				$col->install($table);
			}
			$db_class::onDBInstall($table);
			if (!empty($self->_index)) {
				foreach ($self->_index as $key => $value) {
					if (is_numeric($key)) {
						$table->index("index_$db_class"."_$key",$value);
					}else{
						$table->index($key,$value);
					}
				}
			}
			if (!empty($self->_unique)) {
				foreach ($self->_unique as $key => $value) {
					if (is_numeric($key)) {
						$table->unique("unique_$db_class"."_$key",$value);
					}else{
						$table->unique($key,$value);
					}
				}
			}
			if (!empty($self->_primary)) {
				$table->pri($self->_primary);
			}
		})->install();
	}
	static function defineAllCols($list = null){
		if(is_null($list)) $list = db_table::allClass();
		foreach ($list as $db_class) {
			(new $db_class())->defineAllColsInligne();
		}
	}
	function defineAllColsInligne(){
		$classname = get_class($this);
		if(empty(db_table::$_colsList[$classname])) db_table::$_colsList[$classname] = [];
		$list = get_object_vars($this);
		foreach ($list as $key => $value) {
			if (preg_match("/^col_((?<type>int|string|double|text|date|array|json|timestamp|datetime)_)?(?<name>[a-zA-Z0-9_]*?)$/i", $key, $match)) {
				if (empty($match["type"])) {
					if (is_null($value)) {
						$match["type"] = "string";
					}else{
						$type = gettype($value);
						switch ($type) {
							case 'integer':
								$match["type"] = "int";
							break;
							case 'double':
								$match["type"] = "double";
							break;
							case 'string':
								$match["type"] = "string";
							break;
							default:
								$match["type"] = "text";
							break;
						}
					}
				}
				if (!isset(db_table::$_colsList[$classname][$match["name"]])) {
					$rc = new ReflectionClass("db_table_type_".$match["type"]);
					$args = [$classname,$match["name"],$value];
					$col = $rc->newInstanceArgs($args);
					db_table::$_colsList[$classname] = [$args[1]=>$col]+db_table::$_colsList[$classname];
				}
			}
		}
	}
	static $_colsList = [];
	static function __callStatic($name,$args){
		$classname = get_called_class();
		if (empty(self::$_colsList[$classname])) self::$_colsList[$classname] = [];
		if (preg_match("/col_(?<type>[a-zA-Z0-9_]+)$/i", $name,$match)) {
			if (isset(self::$_colsList[$classname][$args[0]])) {
				return self::$_colsList[$classname][$args[0]];
			}
			$rc = new ReflectionClass("db_table_type_".$match["type"]);
			array_unshift($args, $classname);
			$col = $rc->newInstanceArgs($args);
			$col->form = true;
			$col->col(12,count(self::$_colsList[$classname]) + 1);
			self::$_colsList[$classname][$args[1]] = $col;
			return $col;
		}
	}
 	
 	static $listview = [];
	static function addView($callback){
		$classname = get_called_class();
		if(empty(self::$listview[$classname])) self::$listview[$classname] = [];
		self::$listview[$classname][] = [
			"callback" => $callback
		];
	}
	static function getfilter(){
		$classname = get_called_class();
		$query = new db_table_filter($classname,func_get_args());
		if (array_key_exists("show_by_user", $classname::$_config)) {
			if ((bool)$classname::$_config["show_by_user"]) {
				if (user::get("type")!="Administrateur") {
					$iduser = user::get("id");
					if(is_numeric($iduser)){
						$query = $query->where("user_id=$iduser");
					}
				}
			}
		}
		if (!empty($_REQUEST["rules"])) {
			switch ((int)$_REQUEST["rules"]["show"]) {
				case 0:
					$query = $query->where("0=1");
				break;
				case 1:
					$query = $query->where("iduserapp=".$_SESSION["user"]["id"]);
				break;
				default:
				break;
			}
		}
 
		return $query;
	}
	static function get(){
		return (new db_table_filter(get_called_class(),func_get_args()))->first();
	}
	static function getAll(){
		return (new db_table_filter(get_called_class(),func_get_args()))->all();
	}
	function init(){
		$self = $this;
		get_class($this)::forEachCol(function($col) use($self){
			$col->init($self);
		});
		return $this;
	}
	function load(){
		$classname = get_class($this);
		return $this;
	}
    static function initdbtable(){
    }
}
class db_table_col{
	var $classname = "";
	var $name = "";
	var $type = "";
	var $default = "";
	var $to_class = "";
	var $isLink = false;
	function __construct($classname,$name,$type="string",$default = null){
		
	}
	function on($event,$callback){
		return $this;
	}
	function link($to_class){
		$this->to_class = $to_class;
		$this->isLink = true;
		return $this;
	}
}
class db_table_filter{
	var $_classname = "";
	var $_filterdata = [];
	var $_where = [];
	var $_order = [];
	var $_select = ["*"];
	var $_action = "select";
	var $_args = null;
	var $_page = 1;
	var $_length = 10;
	function __construct($classname,$_filterdata = []){
		$this->_classname = $classname;
		$this->_filterdata = $_filterdata;
	}
	function __call($name,$args){
		array_unshift($args, $name);
		call_user_func_array([$this,"where"], $args);
		return $this;
	}
	function __set($name,$value){
	}
	function __get($name){
	}
	function select($cols="*"){
		$this->_action = "select";
		$this->_select[] = $cols;
		return $this;
	}
	function delete(){
		$this->_action = "delete";
		return $this;
	}
	function update($args = null){
		$this->_args = $args;
		$this->_action = "update";
		return $this;
	}
	function first($args = null){
		if (!is_null($args)) {
			$this->_args = $args;
		}
		return $this->run("first");
	}
	function last($args = null){
		if (!is_null($args)) {
			$this->_args = $args;
		}
		return $this->run("last");
	}
	function all($args = null){
		if (!is_null($args)) {
			$this->_args = $args;
		}
		return $this->run("all");
	}
	function count(){
		$sql = $this->sql($for);
		if(!$sql) return 0;
		$count = db::val("SELECT count(*) as total FROM ($sql) tablecount",$this->_params);
		if(empty($count)) return 0;
		return (int)$count;
	}
	function pagination($page = 1,$length = 10){
		$this->_page = $page;
		$this->_length = $length;
		return $this->run("pagination");
	}
	function where($col,$data = []){
		if(!empty($col)){
			if(is_array($col)){
				$col = "(".join(") AND (",$col).")";
			}
			foreach ($data as $key => $value) {
				if(!preg_match("/^\:/i",$key)) $key = ":$key";
				$this->_params[$key] = $value;
			}
			$this->_where[] = "$col";
		}
		return $this;
	}
	function order($col,$order = "ASC"){
		return $this->orderBy($col,$order);
	}
	var $_limit = "";
	function limit($limit){
		$this->_limit = "limit $limit";
		return $this;
	}
	function orderBy($col,$order = "ASC"){
		$this->_order[] = "$col $order";
		return $this;
	}
	function getTableName(){
		$classname = preg_replace("/^db_/i","", $this->_classname);
		return "¤".strtoupper($classname);
	}
	var $_params = [];
	function setparam($col,$value){
		if (!isset($this->_params[":".$col])) {
			$this->_params[":".$col] = $value;
			return ":".$col;
		}
		if ($this->_params[":".$col] == $value) {
			return ":".$col;
		}
		for ($i=1; $i <= 100; $i++) { 
			if (!isset($this->_params[":".$col."_i$i"])) {
				$this->_params[":".$col."_i$i"] = $value;
				return ":".$col."_i$i";
			}
			if ($this->_params[":".$col."_i$i"] == $value) {
				return ":".$col."_i$i";
			}
		}
	}
	var $_filter = [];
	function filter($filter = []){
		$this->_filter = $filter;
		$cn = $this->_classname;
		$self = $this;
		$cn::forEachCol(function($col) use ($self,$filter){
			$col->sqlFilter($filter,$self);
		});
		return $this;
	}
	function getWhereSql($startwith=""){
		$where = [];
		$vv = true;
		if (!empty($this->_filterdata)) {
			foreach ($this->_filterdata as $filter) {
				if(!is_array($filter)) continue;
				$vv = false;
				$ff = [];
				foreach ($filter as $col => $value) {
					if (is_numeric($col)) {
						if (is_array($value)) {
							$ff[] = $value[0]." ".$value[1]." ".$this->setparam($value[0],$value[2]);
						}else{
							$ff[] = $value;
						}
					}else{
						if (db_table::getCol($col,$this->_classname)) {
							if (is_array($value)) {
								$f = [];
								foreach ($value as $i=>$option) {
									$f[] = $this->setparam($col."_$i",$option);
								}
								if(!empty($f)){
									$ff[] = $col." in (".join(",",$f).")";
								}
							}else{
								$ff[] = $col." = ".$this->setparam($col,$value);
							}
						}
					}
				}
				$ff = array_merge($ff, $this->_where);
				if (!empty($ff)) {
					if(count($ff) == 1){
						$where[] = "(".join(") AND (",$ff).")";
					}else{
						$where[] = "((".join(") AND (",$ff)."))";
					}
					
				}
			}
		}
		if ($vv) {
			if (!empty($this->_where)) {
				if(count($this->_where) == 1){
					$where[] = "(".join(") AND (",$this->_where).")";
				}else{
					$where[] = "((".join(") AND (",$this->_where)."))";
				}
			}
		}
		if (!empty($where)) {
			return trim("$startwith ".join(" OR ",$where));
		}
		return "";
	}
	function sql($for = "all"){
		$sql = "";
		switch ($this->_action) {
			case 'select':
				$selectcols = "*";
				if(empty($this->_select)) $this->_select = ["*"];
				if (is_array($this->_select)) {
					$selectcols = join(",",$this->_select);
				}else{
					$selectcols = $this->_select;
				}
				$sql = "SELECT $selectcols FROM ".$this->getTableName()." tbl ".$this->getWhereSql("where");
				if (!empty($this->_order)){
					$sql .=" order by ".join(",",$this->_order);
				}
				if ($for == "first") {
					$sql .=" Limit 0,1";
				}else{
					$sql .=" ".$this->_limit;
				}
			break;
			case 'update':
				if (empty($this->_args)) {
					return false;
				}
				$setlist = [];
				foreach ($this->_args as $key => $value) {
					if (db_table::getCol($key,$this->_classname)) {
						$setlist[] = "$key = ".$this->setparam($key,$value);
					}
				}
				if(empty($setlist)) return false;
				$setlist = join(",",$setlist);
				$sql = "UPDATE ".$this->getTableName()." tbl SET $setlist ".$this->getWhereSql("WHERE");
				if ($for == "first") {
					$sql .=" Limit 0,1";
				}
			break;
			case 'delete':
				$sql = "DELETE FROM ".$this->getTableName()." ".$this->getWhereSql("WHERE");
				if ($for == "first") {
					$sql .=" Limit 0,1";
				}
			break;
		}
		return $sql;
	}
	function run($for = "all"){
		$sql = $this->sql($for);
		if(!$sql) return $sql;
		$classname = $this->_classname;
		if ($for == "first") {
			if($row = db::row($sql,$this->_params)){
				return (new $classname($row))->init();
			}
		}elseif($for == "pagination"){
			if(floatval($this->_length)<=0) $this->_length = 10;
			$start = (floatval($this->_page) - 1) * floatval($this->_length);
			$length = floatval($this->_length);
			$data = db::table($sql." Limit $start,$length",$this->_params);
			$count = db::val("SELECT count(*) as total FROM ($sql) tablecount",$this->_params);
			if(empty($count)) $count = 0;
			$rep = [];
			foreach ($data as $row) {
				if ($instance = (new $classname($row))->init()) {
					$rep[] = $instance;
				}
			}
			return [
				"page"    => floatval($this->_page),
				"length"  => floatval($this->_length),
				"nbrPage" => ceil(floatval($count) / floatval($this->_length)),
				"total"   => floatval($count),
				"data"    => $rep,
			];
		}else{
			$data = db::table($sql,$this->_params);
			$rep = [];
			foreach ($data as $row) {
				if ($instance = (new $classname($row))->init()) {
					$rep[] = $instance;
				}
			}
			if ($for == "all") {
				return $rep;
			}else{
				if (empty($rep)) {
					return false;
				}
				return $rep[count($rep)-1];
			}
		}
	}
}
class db_table_type{
	var $args = [];
	var $name = "";
	var $classname = "";
	var $type = "string";
	var $length = 255;
	var $default = null;
	var $form = false;
	var $tableName = "";
	function __construct(){
		$this->args      = func_get_args();
		$this->classname = array_shift($this->args);
		$this->tableName = "¤".strtoupper(preg_replace("/^db_/i", "", $this->classname));
		$this->name      = array_shift($this->args);
		$this->default   = array_shift($this->args);
	}
	function onUpdateItem($classname,$item){
	}
	function initItem(){
	}
	var $_data = [];
	function __call($name,$args){
		$this->_data[$name] = $args[0];
		return $this;
	}
	function __set($name,$value){
		$this->_data[$name] = $value;
	}
	function get($names = [],$default = null){
		if(is_string($names)) $names = explode(",",$names);
		foreach ($names as $name) {
			if (isset($this->_data[$name])) {
				return $this->_data[$name];
			}
		}
		return $default;
	}
	function __get($name){
		if (isset($this->_data[$name])) {
			return $this->_data[$name];
		}
		if (preg_match("/^order/i",$name)) {
			return $this->order;
		}
		if ($name == "titre") {
			return $this->name;
		}
		if ($name == "placeholder") {
			return $this->titre;
		}
		if ($name == "filter") {
			return false;
		}
		return null;
	}
	var $validationfn = [];
	function validation($arg){
		if (is_callable($arg)) {
			$this->validationfn[] = $arg;
			return $this;
		}elseif($arg){
			$rep = [];
			foreach ($this->validationfn as $fn) {
				$fn = $fn->bindTo($this);
				$r = $fn($arg,$arg->{$this->name});
				if (!empty($r) && is_string($r)) {
					$r = ["etat"=>false,"msg"=>$r];
				}elseif ($r === true || $r === false) {
					$r = ["etat"=>$r];
				}
				if (is_array($r)) {
					$rep += $r;
				}
			}
			return $rep;
		}
	}
	function beforeSave($elm){
	}
	function tplActions($classname){
	}
	function tplTable($classname){
	}
	var $col = 3;
	var $order = 10;
	function col($col,$order = null){
		$this->col = $col;
		if(!is_null($order)){
			$this->order = $order;
		}
		return $this;
	}
	function on($event,$callback){
		return $this;
	}
	function onCreateFilter($filter){
		$sql = $this->sql;
		if(!empty($sql)){
			$filter->select($sql);
		}
	}
	function install($table,$prefixname = ""){
		$sql = $this->sql;
		if(!empty($sql)) return false;
		if ($this->name == "id" && $this->type == "int") {
			$table->int("id")->pri()->auto_increment();
		}else{
			$table->{$this->type}($prefixname.$this->name)->def($this->default);
			if (is_array($this->prefix)) {
				$table->string($prefixname.$this->name."_prefix");
			}
			if (is_array($this->suffix)) {
				$table->string($prefixname.$this->name."_suffix");
			}
		}
	}
	function tplTableTh(){
		?>
		<th title="<?= $this->tip?$this->tip:"" ?>" ng-hide="filter.<?= $this->name ?>.hide" style="white-space: nowrap;<?= $this->flex?"":"width: 1px;" ?><?= $this->align?"text-align: ".$this->align.";":"" ?>">
			<?= $this->get("titreTh",$this->titre) ?>
		</th>
		<?php
	}
	function tplTableTd(){
		// white-space: nowrap; text-overflow: ellipsis; overflow: hidden;
		?>
		<td ng-hide="filter.<?= $this->name ?>.hide" style="<?= $this->flex?" max-width: 1px;":"width: 1px;" ?><?= $this->align?"text-align: ".$this->align.";":"" ?>;">
			<?= $this->tplTd() ?>
		</td>
		<?php
	}
	function tplTd(){
		$setTplTd = $this->setTplTd;
		if(!empty($setTplTd)){
			if(is_callable($setTplTd)){
				$setTplTd();
			}else{
				echo $setTplTd;
			}
			return;
		}
		$suffix = $this->suffix;
		$prefix = $this->prefix;
		$groupe = (empty($suffix) && empty($prefix))?false:true;
		if ($groupe) {
		?>
		<div style="display: inline-flex;">
		<?php if (!empty($prefix)): ?>
			<?php if (is_array($prefix)): ?>
			<span style="margin-right: 7px;">{{item.<?= $this->name ?>_prefix}}</span>
			<?php else: ?>
			<span style="margin-right: 7px;"><?= $prefix ?></span>
			<?php endif ?>
		<?php endif ?>
		<?php
		}
		?>
		<span title="{{item.<?= $this->name ?>}}">{{item.<?= $this->name ?>}}</span>
		<?php
		if ($groupe) {
		?>
		<?php if (!empty($suffix)): ?>
			<?php if (is_array($suffix)): ?>
				<span style="margin-left: 7px;">{{item.<?= $this->name ?>_suffix}}</span>
			<?php else: ?>
				<span style="margin-left: 7px;"><?= $suffix ?></span>
			<?php endif ?>
		<?php endif ?>
		</div>
		<?php
		}
	}
	function tplInputTag(){
		$readonly = $this->readonly;
		$autocompleteapi=preg_replace("/^db_/i","",$this->classname).":".$this->name;
		?>
		<input type="<?= $this->get("inputType","text") ?>" <?= $readonly?'readonly':'' ?> <?= $this->get("autocomplete",false) ? 'autocompleteapi="'.$autocompleteapi.'"':'' ?> class="form-control" name="<?= $this->name ?>" <?= $this->get("useAsNgModel","ng-model") ?>="item.<?= $this->name ?>" placeholder="<?= $this->placeholder ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>" >
		<?php
	}
	function tplInput(){
		$suffix = $this->suffix;
		$prefix = $this->prefix;
		$groupe = (empty($suffix) && empty($prefix))?false:true;
		if ($groupe) {
		?>
		<div class="input-group">
		<?php if (!empty($prefix)): ?>
			<?php if (is_array($prefix)): ?>
		    <div class="input-group-btn">
		        <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{item.<?= $this->name ?>_prefix}} <span class="caret"></span></button>
		        <ul class="dropdown-menu dropdown-menu-left">
		          <?php 
		          foreach ($prefix as $key => $value) {
		          	?>
		          	<li><a href="#" ng-click="item.<?= $this->name ?>_prefix='<?= $value ?>';<?= $this->onchange ?>"><?= $value ?></a></li>
		          	<?php
		          }
		          ?>
		        </ul>
		    </div>
			<?php else: ?>
				<span class="input-group-addon"><?= $prefix ?></span>
			<?php endif ?>
		<?php endif ?>
		<?php
		}
		$this->tplInputTag();
		if ($groupe) {
		?>
			<?php if (is_array($suffix)): ?>
		    <div class="input-group-btn">
		        <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{item.<?= $this->name ?>_suffix}} <span class="caret"></span></button>
		        <ul class="dropdown-menu dropdown-menu-right">
		          <?php 
		          foreach ($suffix as $key => $value) {
		          	?>
		          	<li><a href="#" ng-click="item.<?= $this->name ?>_suffix='<?= $value ?>';<?= $this->onchange ?>"><?= $value ?></a></li>
		          	<?php
		          }
		          ?>
		          
		        </ul>
		    </div>
			<?php else: ?>
				<span class="input-group-addon"><?= $suffix ?></span>
			<?php endif ?>
		</div>
		<?php
		}
	}
	function tplAction(){
	}
	function tplContent(){
		ob_start();
		?>
		<div class="form-group has-feedback" ng-class="{'has-success':_.etat===true,'has-error':_.etat===false}">
			<label style="display:flex;align-items: center;justify-content: space-between;" class="control-label">
			<?php $this->tplAction() ?>
			<span style="flex:1"><?= ($this->tip ? $this->tip : $this->titre) ?></span>
			</label>
			<?php $this->tplInput() ?>
			<span ng-show="_.etat===true || _.etat===false" class="glyphicon form-control-feedback "  ng-class="{'glyphicon-ok':_.etat===true,'glyphicon-remove':_.etat===false}" aria-hidden="true"></span>
			<span class="help-block" ng-show="_.msg">{{control.<?= $this->name ?>.msg}}</span>
		</div>
		<?php
		return preg_replace("/\t|\n|\r/i","",ob_get_clean());
	}
	function tpl(){
		?>
		<div>
			<?= $this->tplContent() ?>
		</div>
		<?php
	}
	function tplFilter(){
		$cn = str_replace("db_", "", $this->classname);
		?>
		<div class="form-group" ng-hide="control.<?= $this->name ?>.hide">
			<label style="display:block" class="control-label" ng-hide="ctrlff.label"><?= $this->get("titreFilter,titre",$this->name) ?></label>
			<label style="display:block" class="control-label" ng-show="ctrlff.label">{{ctrlff.label}}</label> 
			 
<?php  if($this->useSelectInfilter){ ?>
			<<?= $cn ?>-select empty="-- aucun --"  ng-model="filter.select_<?= $this->name ?>" ng-change="filter.<?= $this->name ?> = (filter.select_<?= $this->name ?>.titre != '-- aucun --' && filter.select_<?= $this->name ?>.titre) || '';filter.loadData()"></<?= $cn ?>-select>
<?php  }else{ ?>
			<input type="text" autocompleteapi="<?= preg_replace("/^db_/i","",$this->classname) ?>:<?= $this->name ?>" class="form-control" name="<?= $this->name ?>" ng-model="filter.<?= $this->name ?>" placeholder="<?= $this->get("placeholderFilter,titreFilter,placeholder,titre",$this->name) ?>" ng-enter="filter.loadData()">
<?php  } ?>
			
 
 
		</div>
		<?php
	}
	function sqlFilter($data,$filter){
		if (!empty($data[$this->name])) {
			$filter->where("$this->name LIKE :filter$this->name",[":filter$this->name"=>"%".$data[$this->name]."%"]);
		}
	}
	function _tpl(){
		ob_start();
		$this->tpl();
		return preg_replace("/\t|\n|\r/i","",ob_get_clean());
	}
	function _tplFilter(){
		ob_start();
		$this->tplFilter();
		return preg_replace("/\t|\n|\r/i","",ob_get_clean());
	}
	function echoJs($parent){
		echo "<script>";
		foreach (["formDirective","filterDirective"] as $fnname) {
			ob_start();
			$this->$fnname($parent);
			echo db_table::rmvTag(ob_get_clean());
		}
		echo "</script>";
	}
	function filterDirective($parent){
		$mdlname = $parent->getDirectiveName();
		?>
		<script>
			app.directive('<?= $mdlname.ucfirst(str_replace("_","",$this->name)) ?>Filter', [function () {
				return {
					restrict: 'E',
					replace : true,
					template : <?= json_encode(preg_replace("/\t|\n|\r/i","",$this->_tplFilter())) ?>,
					scope : {filter:"=",control:"=?",filterData:"=?",label : "@?"},
					link: function ($scope, iElement, iAttrs) {
						$scope.ctrlff = $scope;
						$scope.affectToFilter = function(){
							if($scope.filter && $scope.filterData){
								for(var x in $scope.filterData){
									$scope.filter[x] = $scope.filterData[x];
								}
							}
							if($scope.control && $scope.filter){
								if ($scope.control.<?= $this->name ?>) {
									if ($scope.control.<?= $this->name ?>.hide) {
										$scope.filter.<?= $this->name ?> = $scope.control.<?= $this->name ?>.value;
										if($scope.filter.loadData) $scope.filter.loadData();
									}
								}
							}
						}
						$scope.$watch("filter",function(){
							$scope.affectToFilter();
						});
						$scope.$watch("filterData",function(){
							$scope.affectToFilter();
						});
						$scope.$watch("control.<?= $this->name ?>", function(){
							$scope.affectToFilter();
						});
						$scope.affectToFilter();
					}
				};
			}]);
		</script>
		<?php
	}
	function formDirective($parent){
		$mdlname = $parent->getDirectiveName();
		?>
		<script>
			app.directive('<?= $mdlname.ucfirst(str_replace("_","",$this->name)) ?>', [function () {
				return {
					restrict: 'E',
					replace : true,
					template : <?= json_encode(preg_replace("/\t|\n|\r/i","",$this->_tpl())) ?>,
					scope : {item:"=",control:"=?",controlFilter:"=?",onitemchange:"=?",ctrlForm:"=?"},
					controller : function($scope){
						$scope._ = {};
						$scope.$watch("control",function(){
							$scope._ = $scope._ || {};
							if ($scope.control) {
								$scope._ = $scope.control.<?= $this->name ?> || {};
							}
							if ($scope.controlFilter) {
								$scope._.ctrl = $scope.controlFilter.<?= $this->name ?> || {};
							}
						});
						$scope.$watch("controlFilter",function(){
							$scope._ = $scope._ || {};
							if ($scope.control) {
								$scope._ = $scope.control.<?= $this->name ?> || {};
							}
							if ($scope.controlFilter) {
								$scope._.ctrl = $scope.controlFilter.<?= $this->name ?> || {};
							}
						});
					},
					link: function (scope, iElement, iAttrs) {
						
					}
				};
			}]);
		</script>
		<?php
	}
	function init($elm){
	}
}
Route::isJs(function(){
?>
<script>
	app.directive('autocompleteapi', function ($compile) {
		return {
			restrict: 'A',
			scope:{autocompleteapi:"@"},
			link: function ($scope, iElement, iAttrs) {
				var elm = iElement[0];
				var id = "autocomplete"+uniqid();
				$scope.data = [];
				var list = $compile($('<datalist id="'+id+'"><option ng-repeat="item in data" value="{{item.value}}"></datalist>').get(0))($scope);
				$(list).insertAfter(elm);
				$(elm).attr("list",id);
				var text = $(elm).val();
				var timer = null;
				elm.addEventListener("keyup",function(){
					if(text == $(elm).val()) return;
					text = $(elm).val();
					if(timer) clearTimeout(timer);
					timer = setTimeout(function(){
						if(!$scope.autocompleteapi || $scope.autocompleteapi == "") return;
						if ($scope.autocompleteapi.split(":").length == 2) {
							api("dbtableapidata&classname="+$scope.autocompleteapi.split(":")[0]+"&action=autocomplete",{
								classname:$scope.autocompleteapi.split(":")[0],
								action:"autocomplete",
								data:{name : $scope.autocompleteapi.split(":")[1],q:text}
							},function(rep){
								$scope.data = rep || [];
								$scope.$apply();
							});
						}else{
							api($scope.autocompleteapi,{q:text},function(rep){
								$scope.data = rep || [];
								$scope.$apply();
							});
						}
					},300);
				});
			}
		};
	});
</script>
<?php
});
Route::isJs(function(){
ob_start();
?>
<div>
<ui-select ng-model="ngModel" on-select="selectedelm($item, $model)" theme="bootstrap" style="display: block;width: 100%;">
    <ui-select-match placeholder="{{placeholder}}">{{$select.selected.titre}}</ui-select-match>
    <ui-select-choices style="min-height: 175px;position: relative;" refresh="loadData($select)" refresh-delay="100" repeat="item in mdllist | filter: $select.search" ui-disable-choice="item.loading">
      <div>
      	<div ng-hide="item.loading" ng-bind-html="item.titre | highlight: $select.search"></div>
      	<div ng-show="item.loading" style="padding: 5px 0;position: absolute;top: 0;left: 0;right: 0;bottom: 0;display: flex;align-items: center;justify-content: center;">
      		<md-progress-circular md-mode="indeterminate"></md-progress-circular>
      	</div>
      </div>
    </ui-select-choices>
</ui-select>
</div>
<?php
$code=ob_get_clean();
?>
<script>
	app.factory('$api', function($rootScope,$timeout) {
	    var factory = {}; 
	    factory.loading = false;
	    factory.api = function(classname,apiname,data,callback) {
	    	factory.loading = true;
	    	if(apiname){
				return api("dbtableapidata&classname="+classname+"&action="+apiname,{
					classname:classname,
					action:apiname,
					data:data
				},function(rep){
					factory.loading = false;
					if(callback) callback(rep);
			        $timeout(function() {
			            $rootScope.$apply();
			        });
				});
	    	}else{
				return api(classname,data,function(rep){
					factory.loading = false;
					if(callback) callback(rep);
			        $timeout(function() {
			            $rootScope.$apply();
			        });
				});
	    	}
			
	    };
	    return factory;
	});	
	app.directive('uiselect2', ['$api',function ($api) {
		return {
			restrict: 'E',
			template : <?= json_encode($code) ?>,
			scope : {ngModel:"=",ngChange:"&?",api:"@",placeholder:"@?",empty:"@?",join:"@?"},
			controller : function($scope){
				$scope.loading = false;
				$scope.mdllist = [];
				$scope.selectedelm = function(item){
				}
				$scope.loadData = function($select){
					$scope.loading = true;
					$scope.mdllist = [];
					$scope.mdllist.push({loading : true});
					$api.api($scope.api.split(":")[0],$scope.api.split(":")[1],{q:$select.search,join:$scope.join},function(rep){
						$scope.mdllist = rep;
						$scope.loading = false;
						$scope.$apply();
					});
				}
			},
			link: function (scope, iElement, iAttrs) {
				
			}
		};
	}])
</script>
<?php
});
class db_table_type_date extends db_table_type{
	var $type = "date";
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
	function initItem(){
		$defaultValue = $this->defaultValue;
		if (!empty($defaultValue)) {
			if (is_numeric($defaultValue)) {
				$defaultValue = db::parseDate(date("Y-m-d"),"Y-m-d",floatval($defaultValue));
			}
			?>
			<script>
				item.<?= $this->name ?> = item.<?= $this->name ?> || "<?= $defaultValue ?>";
			</script>
			<?php
		}else{
			return;
			?>
			<script>
				item.<?= $this->name ?> = item.<?= $this->name ?> || "<?= date("Y-m-d") ?>";
			</script>
			<?php
		}
	}
	function tplInputTag(){
		$readonly = $this->readonly;
		?>
		<input type="<?= $this->get("inputType","date") ?>" <?= $readonly?'readonly':'' ?> class="form-control avs" name="<?= $this->name ?>" ng-model="d" mydate="item.<?= $this->name ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>" >
		<?php
	}
	// function install($table,$prefixname = ""){
	// 	clog("install date ".$prefixname.$this->name);
	// 	$table->date($prefixname.$this->name);
	// }
	function init($elm){
	}
	function tplTd(){
		?>
		{{item.<?= $this->name ?>?item.<?= $this->name ?>.split(" ")[0].split("-").reverse().join("/"):''}} 
		{{item.<?= $this->name ?>.split(" ")[1]}}
		<?php
	}
	function tplFilter()
	{
		?>
		<div style="display: flex;">
			<div style="margin: 0;" class="form-group">
				<label style="display:block" class="control-label"><?= $this->titre
																	?></label>
				<select class="form-control avs" ng-model="filter.<?= $this->name
																	?>" ng-change="filter.loadData()">
					<option value="">Tout</option>
					<option value="p1">Aujourd'hui</option>
					<option value="p2">Hier</option>
					<option value="p3">Cette semaine</option>
					<option value="p4">Ce mois</option>
					<option value="p5">Dernière 30 jours</option>
					<option value="p6">Dernière 90 jours</option>
					<option value="p7">Cette année</option>
					<option value="p8">Année précédente</option>
					<option value="p9">Personnaliser</option>
				</select>
			</div>
			<div style="margin: 0;" class="form-group avs" ng-show="filter.<?= $this->name
																			?>=='p9'">
				<label style="display:block" class="control-label">De</label>
				<input type="date" class="form-control avs" ng-model="dd1<?= uniqid() ?>" mydate="filter.<?= $this->name ?>_de" ng-change="filter.loadData()">
			</div>
			<div style="margin: 0;" class="form-group avs" ng-show="filter.<?= $this->name ?>=='p9'">
				<label style="display:block" class="control-label">A</label>
				<input type="date" class="form-control avs" ng-model="dd2<?= uniqid() ?>" mydate="filter.<?= $this->name ?>_a" ng-change="filter.loadData()">
			</div>
		</div>
	<?php
	}
	function sqlFilter($data, $filter)
	{
		if (!empty($data[$this->name])) {
			switch ($data[$this->name]) {
				case 'p1':
					$filter->where("DATE($this->name) = DATE(CURDATE())");
					break;
				case 'p2':
					$filter->where("DATE($this->name) = (DATE(CURDATE()) - INTERVAL 1 DAY)");
					break;
				case 'p3':
					$filter->where("WEEK($this->name,1) = WEEK(DATE(CURDATE()),1) and YEAR($this->name)=YEAR(CURDATE())");
					break;
				case 'p4':
					$filter->where("YEAR($this->name)=YEAR(CURDATE()) and MONTH($this->name)=MONTH(CURDATE())");
					break;
				case 'p5':
					$filter->where("DATE($this->name)>=(DATE(CURDATE()) - INTERVAL 30 DAY)");
					break;
				case 'p6':
					$filter->where("DATE($this->name)>=(DATE(CURDATE()) - INTERVAL 90 DAY)");
					break;
				case 'p7':
					$filter->where("YEAR($this->name)=YEAR(CURDATE())");
					break;
				case 'p8':
					$filter->where("YEAR($this->name)=YEAR(CURDATE())-1");
					break;
				case 'p9':
					if (!empty($data[$this->name . "_de"])) {
						$filter->where("DATE($this->name) >= DATE('" . $data[$this->name . "_de"] . "')");
					}
					if (!empty($data[$this->name . "_a"])) {
						$filter->where("DATE($this->name) <= DATE('" . $data[$this->name . "_a"] . "')");
					}
					break;
				case 'p10':
					$filter->where("timestampdiff(DAY,DATE_FORMAT(date, '%Y-%m-%d'),CURDATE())<7");
					break;

			}
			// $filter->where("$this->name = :filter$this->name",[":filter$this->name"=>$data[$this->name]]);
	
		}
	}
	}
class db_table_type_timestamp extends db_table_type_date{
	var $type = "timestamp";
	function install($table,$prefixname = ""){
		$table->timestamp($prefixname.$this->name)->def("CURRENT_TIMESTAMP");
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
}
class db_table_type_datetime extends db_table_type_date{
	var $type = "datetime";
	function install($table,$prefixname = ""){
		$table->datetime($prefixname.$this->name);
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
}
class db_table_type_double extends db_table_type{
	var $type = "double";
	var $length = null;
	function initItem(){
		$defaultValue = $this->defaultValue;
		$suffix = $this->suffix;
		if (!empty($defaultValue) && is_numeric($defaultValue)) {
			?>
			<script>
				item.<?= $this->name ?> = item.<?= $this->name ?> || <?= $defaultValue  ?> || 0;
				<?php if(is_array($suffix) && !empty($suffix)){ ?>
				item.<?= $this->name ?>_suffix = "<?= array_shift($suffix) ?>";
				<?php } ?>
			</script>
			<?php
		}
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
	function tplInputTag(){
		$readonly = $this->readonly;
		?>
		<input type="number" <?= $readonly?'readonly':'' ?> style="text-align: right;" class="form-control" name="<?= $this->name ?>" ng-model="item.<?= $this->name ?>" placeholder="<?= $this->placeholder ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>" >
		<?php
	}
}
class db_table_type_int extends db_table_type{
	var $type = "int";
	var $length = "11";
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
}
class db_table_type_string extends db_table_type{
	function init($elm){
		
	}
	function tplFilter(){
		$filterTpl = $this->filterTpl;
		if(!$filterTpl) {
			 return parent::tplFilter();
		}
		if(is_callable($filterTpl)){
			$filterTpl = $filterTpl->bindTo($this);
			ob_start();
			$filterTpl();
			$filterTpl=ob_get_clean();
		}
		?>
		<div class="form-group">
			<label style="display:block" class="control-label" ng-hide="ctrlff.label"><?= $this->get("titreFilter,titre",$this->name) ?></label>
			<label style="display:block" class="control-label" ng-show="ctrlff.label">{{ctrlff.label}}</label>
			<div>
				<?= $filterTpl ?>
			</div>			
		</div>
		<?php
	}
	function sqlFilter($data,$filter){
		$filterSql = $this->filterSql;
		if(!$filterSql){
			return parent::sqlFilter($data,$filter);
		}
		if(is_callable($filterSql)){
			$filterSql = $filterSql->bindTo($this);
			$filterSql($data,$filter);
		}
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
}
class db_table_type_file extends db_table_type{
	function tplInputTag(){
		?>
		<upfile 
			ng-model="item.<?= $this->name ?>"
			max="<?= $this->get("max",1) ?>"
			height="<?= $this->get("height",150) ?>"
			width="<?= $this->get("width",150) ?>"
			dist="<?= $this->get("dist","upload") ?>"
			titre="<?= $this->get("titre","file") ?>"
			accept="<?= $this->get("accept","image/*") ?>"
			<?= $this->get("attrs","") ?>
		></upfile>
		<?php
	}
	function tplTd(){
		?>
		<img ng-src="{{item.<?= $this->name ?>}}" style="width: <?= $this->get("tdImgWidth","30px") ?>;height: <?= $this->get("tdImgHeight","30px") ?>;<?= $this->get("cssTdImg","") ?>">
		<?php
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
	function install($table,$prefixname = ""){
		$table->text($prefixname.$this->name);
	}
	function init($elm){
		$max = floatval($this->get("max",1));
		if ($max>1) {
			if (!is_array($elm->{$this->name})) {
				$elm->{$this->name} = [];
			}
		}elseif(!is_string($elm->{$this->name})){
			$elm->{$this->name} = "";
		}
	}
}
class db_table_type_text extends db_table_type{
	function tplInputTag(){
		$readonly = $this->readonly;
		?>
			<textarea <?= $readonly?'readonly':'' ?> rows="<?= $this->rows?$this->rows:5 ?>" type="text" class="form-control" name="<?= $this->name ?>" ng-model="item.<?= $this->name ?>" placeholder="<?= $this->placeholder ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>"></textarea>
		<?php
	}
	function install($table,$prefixname = ""){
		$table->text($prefixname.$this->name);
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
	function init($elm){
		
	}
}
class db_table_type_longtext extends db_table_type{
	function tplInputTag(){
		$readonly = $this->readonly;
		?>
			<textarea <?= $readonly?'readonly':'' ?> rows="<?= $this->rows?$this->rows:5 ?>" type="text" class="form-control" name="<?= $this->name ?>" ng-model="item.<?= $this->name ?>" placeholder="<?= $this->placeholder ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>"></textarea>
		<?php
	}
	function install($table,$prefixname = ""){
		$table->longtext($prefixname.$this->name);
	}
	function init($elm){
		
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
}
class db_table_type_editor extends db_table_type{
	function tplInputTag(){
		$readonly = $this->readonly;
		?>
			<textarea <?= $readonly?'readonly':'' ?> rows="<?= $this->rows?$this->rows:5 ?>" type="text" class="form-control editor" name="<?= $this->name ?>" ng-model="item.<?= $this->name ?>" placeholder="<?= $this->placeholder ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>"></textarea>
		<?php
	}
	function install($table,$prefixname = ""){
		$table->text($prefixname.$this->name);
	}
	function init($elm){
		
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			parent::tplInput();
		}
	}
}
class db_table_type_json extends db_table_type{
	function initItem(){
		$defaultValue = $this->defaultValue;
		?>
		<script>
			item.<?= $this->name ?> = item.<?= $this->name ?> || <?= json_encode($defaultValue) ?>;
		</script>
		<?php
	}
	function tplInput(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}
	}
	function tplTd(){
		$fn = $this->output;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}
	}
	function tplFilter(){
		$filterTpl = $this->filterTpl;
		if(!$filterTpl) return;
		if(is_callable($filterTpl)){
			$filterTpl = $filterTpl->bindTo($this);
			ob_start();
			$filterTpl();
			$filterTpl=ob_get_clean();
		}
		?>
		<div class="form-group">
			<label style="display:block" class="control-label"><?= $this->titre ?></label>
			<div>
				<?= $filterTpl ?>
			</div>			
		</div>
		<?php
	}
	function sqlFilter($data,$filter){
		$filterSql = $this->filterSql;
		if(!$filterSql) return;
		if(is_callable($filterSql)){
			$filterSql = $filterSql->bindTo($this);
			$filterSql($data,$filter);
		}
	}
	function install($table,$prefixname = ""){
		$table->text($prefixname.$this->name);
	}
	function init($elm){
		
	}
}
class db_table_type_bool extends db_table_type{
	function tplInputTag(){
		$readonly = $this->readonly;
		?>
			<md-switch <?= $readonly?'readonly':'' ?> ng-model="item.<?= $this->name ?>" style="margin: 8px 0;" aria-label="<?= $this->titre ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>"></md-switch>
		<?php
	}
	function tplFilter(){
		?>
		<div class="form-group">
			<label style="display:block" class="control-label"><?= $this->titre ?></label>
			<select class="form-control avs" ng-model="filter.<?= $this->name ?>" ng-change="filter.loadData()">
				<option value="">Tout</option>
				<option value="1">Oui</option>
				<option value="0">Non</option>
			</select>
		</div>
		<?php
	}
	function __get($name){
		$v = parent::__get($name);
		if ($name == "align") {
			if (is_null($v)) {
				return "right";
			}
		}
		return $v;
	}
	function tplTd(){
		?>
		<span class="label" ng-class="{'label-success':item.<?= $this->name ?>,'label-danger':!item.<?= $this->name ?>}">{{item.<?= $this->name ?>?'Oui':'Non'}}</span>
		<?php
	}
	function sqlFilter($data,$filter){
		if (isset($data[$this->name]) && ($data[$this->name]=="1" || $data[$this->name]=="0")) {
			if((bool)$data[$this->name]){
				$filter->where("IFNULL($this->name,0)=1");
			}else{
				$filter->where("IFNULL($this->name,0)=0");
			}
		}
	}
	function install($table,$prefixname = ""){
		$table->int($prefixname.$this->name);
	}
	function init($elm){
		$elm->{$this->name} = (bool)$elm->{$this->name};
	}
	function beforeSave($elm){
		$elm->{$this->name} = (bool)$elm->{$this->name}?1:0;
	}
}
class db_table_type_link extends db_table_type{
	var $ins = null;
	function tplInputTag(){
		$fn = $this->input;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
		?>
			<div>
				<<?= $this->name ?>-select empty="-- aucun --" ng-hide="_.ctrl.hide" default="_.ctrl.value" ng-model="item.<?= $this->name ?>" join="<?= join(",",is_array($this->joinlist)?$this->joinlist:[]) ?>" onitemchange="onitemchange" ng-change="<?= $this->onchange ?>"></<?= $this->name ?>-select>
				<div ng-show="_.ctrl.hide">
					<input type="text" class="form-control" readonly="true" ng-model="item.<?= $this->name ?>.titre">
				</div>
			</div>
		<?php
		}
	}
	function tplAction(){
			$cn = $this->classname;
			$mdlname = db_table::from_camel_case($cn::_getDirectiveName());
			if($this->get("hideadd",false)) return false;
?>
		<button class="btn btn-xs btn-default" ng-click="linknew<?= $this->name ?>.add()" style="margin-right: 10px;zoom:0.8" ng-hide="_.ctrl.hide">
			<span class="glyphicon glyphicon-plus"></span> 
			<!-- Nouveau -->
		</button>
		<div style="display: none;">
		<<?= $this->name ?>-api-data ng-model="linknew<?= $this->name ?>" on-add="item.<?= $this->name ?> = elm;item.<?= $this->name ?>.titre = elm.<?= ("db_".$this->name)::param("col_titre","titre") ?>;" autoload="false"></<?= $this->name ?>-api-data>
		</div>
<?php
	}
	function __construct(){
		$this->args = func_get_args();
		$this->classname = array_shift($this->args);
		$this->tableName = "¤".strtoupper(preg_replace("/^db_/i", "", $this->classname));
		$this->name = array_shift($this->args);
		$this->joinlist = array_shift($this->args);
		if(is_string($this->joinlist)) $this->joinlist = explode(",",$this->joinlist);
	}
	function install($table,$prefixname = ""){
		$classname = "db_".$this->name;
		$table->int($prefixname."id_".$this->name);
		$table->string($prefixname."titre_".$this->name);
		if(!empty($this->joinlist) && is_array($this->joinlist))
		foreach ($this->joinlist as $joincol) {
			if($col = $classname::getCol($joincol)){
				$col->install($table,$prefixname.$this->name."_");
			}
		}
	}
	function init($elm){
		$classname = "db_".$this->name;
		$data = [];
		$data["id"] = $elm->{"id_".$this->name};
		if (is_numeric($data["id"])) {
			if (floatval($data["id"])>0) {
				$data["titre"] = $elm->{"titre_".$this->name};
				if(!empty($this->joinlist) && is_array($this->joinlist))
				foreach ($this->joinlist as $joincol) {
					$data[$joincol] = $elm->{$this->name."_".$joincol};
				}
				$elm->{$this->name} = new $classname($data);
			}
		}
	}
	function beforeSave($elm){
		$data = $elm->{$this->name};
		if (!empty($data)) {
			if ((bool)$data["empty"]) {
				$elm->{"id_".$this->name} = 0;
				$elm->{"titre_".$this->name} = "";
				if(!empty($this->joinlist) && is_array($this->joinlist))
				foreach ($this->joinlist as $joincol) {
					$elm->{$this->name."_".$joincol} = "null";
				}
			}else{
				$elm->{"id_".$this->name} = $data["id"];
				$elm->{"titre_".$this->name} = $data["titre"];
				if(!empty($this->joinlist) && is_array($this->joinlist))
				foreach ($this->joinlist as $joincol) {
					$elm->{$this->name."_".$joincol} = $data[$joincol];
				}
			}
		}
	}
	function onUpdateItem($classname,$item){
		if ($classname == "db_".$this->name && is_numeric($item->id)) {
			$colname = $classname::$_config["col_titre"];
			$cols = [];
			$cols["titre_$this->name"] = $item->$colname;
			$joinlist = $this->joinlist;
			if(is_array($joinlist))
			foreach ($joinlist as $joincol) {
				$cols[$this->name."_".$joincol] = $item->$joincol;
			}
			$d = [];
			$s = [];
			foreach ($cols as $key => $value) {
				$s[] = "$key = :$key";
				$d[":$key"] = $value;
			}
			$s = join(",",$s);
			db::query("UPDATE $this->tableName SET $s where id_$this->name=$item->id",$d);
		}
	}
	function tplActions($classname){
		$titreAction = $this->titreAction;
		$useLink = $this->useLink;
		if ($classname == "db_".$this->name && !empty($titreAction)) {
			if (empty($useLink)) {
				?> 	
				<button class="btn btn-xs" ng-click="link<?= $this->classname ?>.item = item;link<?= $this->classname ?>.show()">
					<?= $this->iconAction ?>
					<?= $this->titreAction ?>
				</button>
				<?php
			}else{
			?>
				<button class="btn btn-xs" ng-click="location('<?= $useLink ?>?<?= $this->name."=" ?>'+item.id)">
					<?= $this->iconAction ?>
					<?= $this->titreAction ?>
				</button>
			<?php
			}
		}
	}
	function tplTable($classname){
		if ($classname == "db_".$this->name) {
			$cn = $this->classname;
			$mdlname = db_table::from_camel_case($cn::_getDirectiveName());
		?>
			<dialog ng-model="link<?= $this->classname ?>" size="<?= $this->classname::$_config["dialog_size"] ?>" width="<?= $this->classname::$_config["dialog_width"] ?>" titre="<?= $this->titreAction ?>" btn-fermer="true">
				<div ng-if="link<?= $this->classname ?>.item && link<?= $this->classname ?>.visible">
					<<?= $mdlname ?> filter="{<?= $this->name ?>:{hide:true,value:{id:link<?= $this->classname ?>.item.id}}}"  rules='<?= $this->get("rules","true") ?>' ></<?= $mdlname ?>>	
				</div>
				<div ng-if="!(link<?= $this->classname ?>.item && link<?= $this->classname ?>.visible)" style="min-height: 250px;display: flex;align-items: center;justify-content: center;">
					<md-progress-circular md-mode="indeterminate" md-diameter="48"></md-progress-circular>
				</div>
			</dialog>
		<?php
		}
	}
/*
	function controlFilterValue(){
		ob_start();
		?>
		<script>
			$scope.filter.<?= $this->name ?>.id = $scope.control.<?= $this->name ?>.value.id;
		</script>
		<?php
		db_table::rmvTag(ob_get_clean());
	}
*/
	function tplFilter(){
		?>
		<div class="form-group" ng-hide="control.<?= $this->name ?>.hide">
			<label style="display:block" class="control-label"><?= $this->titre ?></label>
<?php 
		$tplfilter = $this->get("setTplFilter",null);
		if(is_callable($tplfilter)){
			$tplfilter();
		}else{
?>
			<<?= $this->name ?>-select empty="Tout" ng-model="filter.<?= $this->name ?>"  ng-change="filter.loadData()" <?= $this->get("attrs","") ?> is-filter="true" ></<?= $this->name ?>-select>
<?php
		}
?>
		
		</div>
		<?php
	}
	function sqlFilter($data,$filter){
		if (!empty($data[$this->name])) {
			if (!(bool)$data[$this->name]["empty"]) {
				$filter->where("id_$this->name = :filter$this->name",[":filter$this->name"=>$data[$this->name]["id"]]);
			}
		}
	}
	function tplTd(){
		$setTplTd = $this->setTplTd;
		if(!empty($setTplTd)){
			if(is_callable($setTplTd)){
				$setTplTd();
			}else{
				echo $setTplTd;
			}
			return;
		}
		$suffix = $this->suffix;
		$prefix = $this->prefix;
		$groupe = (empty($suffix) && empty($prefix))?false:true;
		if ($groupe) {
		?>
		<div style="display: inline-flex;">
		<?php if (!empty($prefix)): ?>
			<span style="margin-right: 7px;"><?= $prefix ?></span>
		<?php endif ?>
		<?php } ?>
		{{item.<?= $this->name ?>.titre}} 
		<?php if ($groupe) { ?>
		<?php if (!empty($suffix)): ?>
			<span style="margin-left: 7px;"><?= $suffix ?></span>
		<?php endif ?>
		</div>
		<?php
		}
	}
}
class db_table_type_select extends db_table_type{
	function initItem(){
		$defaultValue = $this->defaultValue;
		?>
		<script>
			item.<?= $this->name ?> = item.<?= $this->name ?> || "<?= $defaultValue ?>";
		</script>
		<?php
	}
	function tplInputTag(){
	    $readonly = $this->readonly;
		?>
			<select <?= $readonly?'readonly':'' ?> <?= $this->ctn?"ctn":"" ?> class="form-control" ng-model="item.<?= $this->name ?>" <?= $this->attrs ?> ng-change="<?= $this->onchange ?>">
				<?php 
				if(is_array($this->options))
				foreach ($this->options as $key => $value) {
					?>
					<option value="<?= is_string($key)?$key:$value ?>"><?= $value ?></option>
					<?php
				}
				?>
			</select>
		<?php
	}
	function tplFilter(){
		?>
		<div class="form-group">
			<label style="display:block" class="control-label"><?= $this->titre ?></label>
			<select class="form-control avs" <?= $this->ctn?"ctn":"" ?> ng-model="filter.<?= $this->name ?>" ng-change="filter.loadData()">
				<option value="">Tout</option>
				<?php 
				if(is_array($this->options))
				foreach ($this->options as $key => $value) {
					?>
					<option value="<?= is_string($key)?$key:$value ?>"><?= $value ?></option>
					<?php
				}
				?>
			</select>
		</div>
		<?php
	}
	function sqlFilter($data,$filter){
		if (!empty($data[$this->name])) {
			$filter->where("$this->name = :filter$this->name",[":filter$this->name"=>$data[$this->name]]);
		}
	}
}
class db_table_type_list extends db_table_type{
	function initItem(){
		$defaultValue = $this->defaultValue;
		?>
		<script>
			item.<?= $this->name ?> = item.<?= $this->name ?> || [];
		</script>
		<?php
	}
	
	function tplInputTag(){
		$tplinputfn = $this->get("input",null);
		$tplinputThfn = $this->get("inputTh",null);
		$tplinputTdfn = $this->get("inputTd",null);
		if(is_callable($tplinputfn)){
			$tplinputfn();
		}elseif(is_callable($tplinputThfn) && is_callable($tplinputTdfn)){
		?>
		<table class="table table-striped table-bordered">
			<tr>
				<?= $tplinputThfn() ?>
				<th style="width: 1px"></th>
			</tr>
			<tr ng-repeat="elm in item.<?= $this->name ?> track by $index">
				<?= $tplinputTdfn() ?>
				<th style="width: 1px;white-space: nowrap;">
					<button class="btn btn-xs" ng-click="item.<?= $this->name ?>.splice($index,1)"><span class="glyphicon glyphicon-trash"></span></button>
				</th>
			</tr>
		</table>
		<div>
			<button class="btn btn-xs" ng-click="item.<?= $this->name ?>=item.<?= $this->name ?>||[];item.<?= $this->name ?>.push({})"> <span class="glyphicon glyphicon-plus"></span> Ajouter</button>
		</div>
		<?php
		}
	}
	function install($table,$prefixname = ""){
		$self = $this;
		$tablename = strtoupper($this->tableName)."_LINK_".strtoupper($this->name);
		db::create($tablename,function($tbl) use($self){
			$tbl->id();
			$tbl->int("id_parent");
			$tbl->int("index_list");
			$fn = $self->get("onInstall",function($t){});
			if(is_callable($fn))
			$fn($tbl);
		})->install();
	}
	function init($elm){
		$tablename = strtoupper($this->tableName)."_LINK_".strtoupper($this->name);
		$id = $elm->id;
		if(!is_numeric($id)) $id = 0;
		$elm->{$this->name} = db::table("SELECT * FROM $tablename where id_parent = $id order by id ASC");
	}
	function afterSave($elm){
		$tablename = strtoupper($this->tableName)."_LINK_".strtoupper($this->name);
		$list = $elm->{$this->name};
		if(!is_array($list)) $list = [];
		$id = $elm->id;
		if(is_numeric($id)){
			db::query("DELETE FROM $tablename where id_parent = $id");
			foreach ($list as $index => $row) {
				if (is_array($row)) {
					$row["id_parent"] = $id;
					$row["index_list"] = $index;
					db::save($tablename,$row);
				}
			}
		}
	}
}
// db_ticket::col_link("db_user","nom,login,photo");
// db_ticket::col_string("email")->on("load",function(){
// 	if (empty($this->email)) {
// 		$this->email = "wassim.dev@gmail.com";
// 	}
// });
