App.Modul.Fo = 1

function MakeApp(JS) {
	var FoJS, NomFo, T, Fo, S, J, O, Opt
	if (!JS)  return
	if (typeof(JS)=='string') {
		try {
			JS = JSOND(JS);  	//alert(JSW(JS))
		}catch(e){
			alert("Parse données impossible"); return
		}
	}
	var Forms = JS['Forms'];  Opt = {}
	for (NomFo in Forms) {
		FoJS = Forms[NomFo];		//alert(JSW(FoJS))
		if (FoJS) {
			if (FoJS.Table) {
				T  = new TableClass(FoJS.Table, FoJS.NomC, FoJS.TypCle, FoJS.ValCle)
				T.ImportDataJS(FoJS.Data)
			}
			Opt = FoJS['Opt'] || {};  Opt.Selects = FoJS['Selects']
		}
		Fo = new FoClass(NomFo, NomFo, T, Opt)
	}
	//DBug.innerText = Fo
}

var FoGen = function(btn, Pgm) {
	var F = ElemPVar(btn,'Fo');  if (!F.Fo)  return;    //alert(Fo.Nom+crr+Lout(F))
	if (Pgm)  F.Fo[Pgm]()
	return F.Fo
}

var getFo = function(btn) { var F = ElemPVar(btn,'Fo');  return F.Fo }


// ====================================      FO       ===============================
var Forms = {
	all: {},
	load: {},
	Sel: null,
	Old: null
}

var FoClass = function(Div, Proto) {
	var id, O, po_Table, S
	Div = LID(Div);  if (!Div)  return
	id = Div.id
	Forms.all[id] = this
	Div.Fo = this
	if (Proto) {
		//if ((O=Proto.Table)) { po_Table = new ObjClone(O);  delete(Proto.Table) }
		if ((po_Table=Proto.Table))  delete(Proto.Table)
		for (var K in Proto)  this[K] = Proto[K]
	}

	this.Nom		= id
	this.Div		= Div
	this.initie = false
	if (this.Table)
		this.TableAutoCre = false
	else{
		this.Table = new TableClass(id,po_Table)
		this.TableAutoCre = true
	}
	if (this.Table)  this.Table.Fo = this
	if ((S=LX(Div,'divAide')))  this.divAide = LID(S)
	this.all		= []
	//this.Typfo  = Opt.Typfo
	if (!this.CtlClass)  this.CtlClass = CtlClass  // prototype différent
	this.InitFo()
	this.gereAffich()
	//alert(this+crr+this.Table)
	this.finLoad()
	return this
}

FoClass.prototype.finLoad = function() {}

FoClass.prototype.InitFo = function(Div) {
	var Cto, Cs=[], Ctl, Nom, Os=[], O1, O2, O3, i, j, T, T2, Ch, Tag, S
	if (!Div)  Div = this.Div;  else  Div = LID(Div);  if (!Div)  return
	if (Div.tagName=='FORM') {
		T = this.Div.elements;  for (i=0; i<T.length; i++)  Os.push(T[i])
	}else{
		O1 = LTagt(Div,'INPUT');  O2 = LTagt(Div,'SELECT');  O3 = LTagt(Div,'TEXTAREA')
		Os = O1.concat(O2,O3)
	}
	O1 = LTags(Div,'SPAN');
	for (i=0; i<O1.length; i++) { if (LX(O1[i],'bd'))  Os.push(O1[i]) }
	for (i=0; i<Os.length; i++) { O1 = Os[i];  if ((S=LX(O1,'name')))  Cs[S] = O1 }
	this.Ctrls = Cs

	T = this.Table
	for (i=0; i<Os.length; i++) {
		Ctl = Os[i]
		Tag = Ctl.tagName
		if ("BUTTON,FIELDSET,EMBED,OBJECT".indexOf(Tag)>=0 || (Ctl.type && Ctl.type=='button'))  continue
		Nom = LX(Ctl,'name');  if (!Nom)  continue
		if (T && !T.initie)  Ch = T.Add(Nom)
		Cto = this.Add(Nom, Ctl);  if (!Cto)  continue;   // éviter les ctl sans name ou id
		this.all[Nom] = Cto

		S = Ctl.id || Nom
		if (!Cto.Label) {
			if (!(O1=$('label[forhl|=lb'+S+']').get(0)))  if (!(O1=$('label[for|='+S+']').get(0)))  O1 = LTags(Ctl.parentNode,'LABEL',0);		//if (O1) alert(Lout(O1)+crr+Lout(Ctl))
			Cto.Label = O1
    }

		if (Tag=='SELECT') {
			if ((S=LX(Ctl,'Ajout')))  Cto.setCB(S)
			if ((S=LX(Ctl,'setCB'))) { //ActionSql
				Cto.setCB(JST(S))
			}else{
				if ((O1=this.getCtl(LX(Ctl,'Source')))) { Hide(Ctl,'DIV');  Aff(O1,'DIV'); }
			}
		}else if (Ctl.type=='radio' && (!Ctl.value || Ctl.value=='on')) {
			if ((O1=$('label[for|='+Ctl.id+']').get(0)))  Ctl.value = LText(O1)
		}
		if (!LVal(Ctl) && (S=LX(Ctl,'Msg'))) { Ctl.value=S;  Cto.MsgIn = S }
		if ((S=LX(Ctl,'Verif'))) {
			T2 = S.split(',');
			for (j in T2) {
				switch(T2[j]) {
				  case 'NonVide':  Cto.NonVide = 1;  break
				  case 'IP':  Cto.Verif_IP = 1;  if (Cto.TypAff=='D')  Cto.Verif_Date = 1;  break
				  case 'eMail':  Cto.Verif_eMail = 1;  break
				}
			}
		}
	}
	for (Nom in this.all) { Cto = this.all[Nom]; if (!Cto.Champ)  Cto.ValDef() }    // pour la prise en compte des meta au cas où le champ JS n'était pas encore rempli au ValDef de création
	if (this.TableAutoCre) {
		if ((Cto=this.all['_NomT']))	T.NomT = Cto.Val()
		if ((Cto=this.all['_NomC'])) { T.NomC = Cto.Val();  T.ChCle = T.all[T.NomC]; }
		if ((Cto=this.all['_TypCle']))	T.TypCle = Cto.Val()
		if ((Cto=this.all['ValCle']))		T.ValCle = Cto.Val()
	}
}

FoClass.prototype.Destroy = function(Div) {  // Détruit les Cto et Ctrl de la Div mais pas les Table.Ch
	var T, Ctl, Cto, i, S
	if (!Div)  Div = this.Div;  else  Div = LID(Div);  if (!Div)  return
	T = ListCtl(Div)
	for (i=0; i<T.length; i++) {
		Ctl = T[i]
		if ((Cto=Ctl.Cto)) {
			Cto.Destroy();  delete(this.all[Cto.id])
		}
	}
}

FoClass.prototype.Add = function(Nom, Ctl) {
	var Cto, n
	Ctl = Ctl || Nom;  if (isStr(Ctl))  Ctl = LID(Ctl)
	if ((Cto=this.all[Nom]))  Cto.Init(Ctl);  else  Cto = new this.CtlClass(this, Nom, Ctl)
	return Cto
}

FoClass.prototype.Champ = function(Nom) {
	if (this.Table)  return this.Table.all[Nom]
}

FoClass.prototype.ValDef = function() {
	var Cto, Nom
	for (Nom in this.all) { Cto = this.all[Nom]; Cto.ValDef() }
}

FoClass.prototype.getCtl = function(Nom) {
	var Cto, Ctl, S, J
	if (typeof(Nom)=='string') {
		if ((O=this.all[Nom]))  Ctl = O.Ctrl;  else  Ctl = this.Ctrls[Nom]
	}else
		Ctl = Nom
	return Ctl
}

FoClass.prototype.gereAffich = function() {
	var Cto, Ctl, D, OL, sp, Nom, id
	for (Nom in this.all) {
		Cto  = this.all[Nom];  Ctl = Cto.Ctrl;
		//id = Ctl.id || Nom
		//if (!(OL=$('label[forhl|=lb'+id+']').get(0))) {
		//	D = Ctl.parentNode
		//	OL = LTags(D,'LABEL',0)
		//}
		//if (OL) alert(Lout(OL)+crr+Lout(Ctl))
		//Cto.Label = OL
		if ((OL=Cto.Label)) {
			sp = ElemIn(OL, "@C='Etoile'", true, 0);
			if (Cto.NonVide) {
				if (!sp)  InsertHtml("<span class=Etoile>&nbsp;*</span>", OL, 'span')
			}else if (sp)
			  RMNod(sp)
		}
	}
}

FoClass.prototype.Verif = function() {
	var Nom, Msg='', Cto;
	for (Nom in this.all) {
		Cto = this.all[Nom];  Msg += Cto.Verif()
	}
	return Msg
}

FoClass.prototype.Url = function() {
	var O, Cto, Ctl, re='', V, Nom
	var ValCle = this.ValCle
	for (Nom in this.all) {
		Cto  = this.all[Nom];  Ctl = Cto.Ctrl;  if (LX(Ctl,'noUrl'))  continue;
		V = Cto.Val()
		if (V) {
			re+='&'+Nom+'='+encodeURIComponent(V)
		}
	}
	return re
}

FoClass.prototype.Rech = function()   {
	var Li, a, O
	Li = this.Url();  if (!Li)  return
	O = this.all['_action'];  a = O.Val();		//alert(a+crr+Li)
	ExecAction(a, Li)
}

FoClass.prototype.PreparSov = function() {
	Edit_to_Memo()
}
FoClass.prototype.Sov = function(Opt)   { if (this.Table)  this.Table.Sov(Opt) }
FoClass.prototype.Suppr = function() { if (this.Table)  this.Table.Suppr() }

FoClass.prototype.toJS = function() {  return this.Table.toJS() }

FoClass.prototype.toString = function() {
	var C, H='', Nom
	for (Nom in this.all) {
		C = this.all[Nom]
		H+= Nom+', '+C.V+cr
	}
	return H+cr
}


// ====================================      CTL       ===============================
var CtlClass = function (Fo, Nom, Ch, TypAff) {
	var id, Ctl, T, Lib, O
	if (typeof(Ch)=='string')  Ctl = LID(Ch);  else  Ctl = Ch
	if (!(id=LX(Ctl,'name')))  id = Ctl.id
	if (!Fo.all[id]) {
		if (!TypAff)  TypAff = LX(Ctl,'TypAff')

		var NomR = Nom;  if (Right(Nom,2)=='[]')  NomR = Isole(Nom,'[',1)
		if (!(Lib=LX(Ctl,"Lib")))  Lib = LX(Ctl,"title");
		if (!Lib) { O = Elem(Ctl, "O.tagName=='LABEL'", true);	 if (O)  Lib = LText(O); }
		if (!Lib)  Lib = NomR
		this.divAide = $('.aideCh',Ctl.parentNode).get(0)

		this.id			= id
		this.Fo			= Fo
		this.all    = {}  // liste des ctrl (radio=plusieurs)
		this.Nom		= Nom
		this.NomR		= NomR
		this.Lib		= Lib
		this.TypAff	= TypAff
		this.Typ2		= LX(Ctl,'Typ2')
		this.bd			= ze(LX(Ctl,'bd'))
		this.Ctrl		= Ctl
		this.MsgIn	= null
		this.V			= null
		this.Champ	= null
		this.NonVide = this.Verif_IP = this.Verif_eMail = 0

		if ((T=this.Fo.Table)) {
			if ((Ch=T.all[Nom])) {
				this.Champ = Ch;  Ch.Cto = this;  Ch.Ctrl = this.Ctrl
			}
		}
		this.ValDef()
	}else{
	  this.id = '';   // pour éviter les doublons, par exemple sur les RADIO
	}
	this.Init()
}

CtlClass.prototype.Init = function(Ctl) {
	var Fo, O, S
	Fo  = this.Fo;  Ctl = Ctl || this.Ctrl
	Ctl.Cto = this;  if (!(S=(Ctl.id || Ctl.name)))  return
	this.all[S] = Ctl
	if (Ctl.type=='checkbox' || Ctl.type=='radio') { if (!Ctl.onclick)  Ctl.onclick = onChange; }else if (!Ctl.onchange)  Ctl.onchange = onChange
	if (!Ctl.onfocus)  Ctl.onfocus = onFocus
	if (!Ctl.onblur)  Ctl.onblur = onBlur
}

CtlClass.prototype.Destroy = function() {
	var Ctl, S
	Ctl = this.Ctrl;  Ctl.onclick = Ctl.onchange = Ctl.onfocus = Ctl.onblur = null
	this.Champ.Ctrl = null
	RMNod(Ctl)
}

CtlClass.prototype.ValDef = function(Opt) {
	var V, S, Ctl, Ch
	Ctl = this.Ctrl;		//alert(Lout(Ctl))
	if (!Opt)  Opt = {}
	if (!Opt.sansMeta && (S=LX(Ctl,'meta'))) {
	  //if ((Ch=this.Fo.Ctrls[S]))  PVal(Ctl, Meta(Ch, this.Nom))
	  if ((Ch=this.Fo.all[S]))  PVal(Ctl, Meta(Ch.Ctrl, this.Nom))
		//wh("ValDef: "+this.Nom+', meta='+S)
	}
	V = LVal(Ctl)
	Ch = this.Champ
	if (!V) {
		if ((S=LX(Ctl,'Valeur'))) {   // Contrairement à ValDef, Valeur ne dépend pas de ValCle
			if (S.substr(0,1)=='=')  V = Eval(S.substr(1));  else  V = S
		  if (!Ch || (Ch && !Ch.Table.ValCle))  PX(Ctl,'adUrl',1)
		}
		if (!Ch || (Ch && !Ch.Table.ValCle)) {
			if ((S=LX(Ctl,'ValDef'))) {
				if (S.substr(0,1)=='=')  V = Eval(S.substr(1));  else  V = S
			}
		   if (this.Fo.Typfo=='Rech')  PX(Ctl,'adUrl',2)  //!!!!! Suppr tempo parce que quand ValCle est défini après, tout est avec adUrl et donc Table.Url prend tout
		}
		if (Ch && (S=Ch.V))  V = S
		//if (Ch)  alert(Ch.Nom+cr+Ch.V)
	}else{
	  if (this.Fo.TableAutoCre) { Ch.Val(V);  Ch.oldV=V }
	}
  if (Ch && !Ch.Table.ValCle && !Ch.V)  { Ch.Val(V);  Ch.oldV=V }  // placé pour Roche / Html.htm / contact_activ /
	PVal(Ctl,V);
}

CtlClass.prototype.Format = function() {
	var O, V, C, Ch, Typ, TypAff, Opt={}
	O = this.Ctrl
	TypAff = this.TypAff
	if ((Ch=this.Champ)) { Typ = Ch.Typ; }
	V = Trim(LVal(O))
	V = FormatChamp(V, Typ, TypAff, Opt)
	return V
}

CtlClass.prototype.Verif = function() {
	var V, S, Msg="", Termin="", i, j, p, l, Err, T, O2

	var Ctl = this.Ctrl;
	V = LVal(Ctl)
	if (this.NonVide) {
		Err = false
		switch (Ctl.type) {
			case "radio":
				//var doc_ctl;  if (ie)  doc_ctl = Ctl.document;  else  doc_ctl = document;  if (Ctl==doc_ctl.getElementsByName(Ctl.name)[0] && !LitRadio(Ctl))  Err=true;  break
				if (!LitRadio(Ctl))  Err=true;  break
			case "checkbox":  if (!Ctl.checked)  Err=true;  break
			default:  if (!Ctl.value || ((S=LX(Ctl,'Msg')) && V==S))  Err=true
		}
		if (Err) {
			if (Ctl.type=="checkbox") { Msg = "La case à cocher ";  Termin="e" }
			Msg+= "'" + this.Lib + "' doit être renseigné"
			Msg+= Termin + '\n'
		}
	}
	if (this.Verif_IP)  if ((S=Verif_IP(V)))  Msg+= S+'\n'
	if (this.Verif_Date || (V && this.TypAff=='D'))  if ((S=Verif_Date(V)))  Msg+= S+'\n'
	if (this.Verif_eMail)  if ((S=Verif_eMail(V)))  Msg+= S+'\n'
	return Msg
}

CtlClass.prototype.Traite = function() {  // Traite un champ, en principe après la maj d'un autre champ Source
	var Ctl, T, Sql, S, i, Cto, V, re, n
	Ctl = this.Ctrl
	// CB Ajax
	if ((Sql=LX(Ctl,'Data'))) {
		if ((T=LX(Ctl,'Source').split(','))) {
			re = Sql.split('|')
			Sql = "SELECT " + re[1] + ', ' + re[2] + " FROM " + re[0] + " WHERE " + re[4];		//alert(Sql+crr+JSW(re))
			re = Sql.match(/@[0-9]+/g)
			if (re) {
				for (i=0; i<re.length; i++) {
				  V = ''
					S = re[i].substr(1)-1;		//alert(i+':'+re[i]+','+T[S]);
					if (T[S] && (Cto=this.Fo.all[T[S]]))  V = Cto.Val()
					Sql = Replace(Sql,re[i],V);   //alert(Sql+AjUrl+cr+V)
					var NomC = Mot(Sql,2)
					var Param = { Cto:this }
					var Pgm = function(re, Param) {
					  var JS, C
						var Cto = Param.Cto;  C = Cto.Ctrl
						JS = JSOND(re)
						Aff(C,'DIV');  Hide(LX(Ctl,'Source'),'DIV')
						Focus(C)
						setComboBox(C, JS, {ValDef:null,Clear:1,AjFin:['(Nouvelle sélection)']});		//alert(Lout(C))
						n = C.options.length - 1
						if (n!=1)  setComboBox(C, ['('+ n + ' correspondances)'],{pos:0})
						//C.selectedIndex = 0
					}
					XmlPost('&Pgm=LitSql&NomC='+NomC+'&Sql='+encodeURIComponent(Sql)+AjUrl, '', Pgm, Param)
				}
			}
		}
	}
}

CtlClass.prototype.Change = function() {
	var Cto, Ctl, M, S, V;      //alert(Lout(this.Ctrl))
	V = this.Format()
	this.Val(V)
	Ctl = this.Ctrl
	if ((S=Ctl.ChSuit)) {
		if (S.Cto) {
			S.Cto.Traite()
		}
	}
	if ((S=Ctl.Source)) {
		if ((Cto=S.Cto)) {
			if (V==0) { Aff(Cto.Ctrl,'DIV');  Hide(Ctl,'DIV');  Focus(Cto.Ctrl) }
		}
	}
	this.Meta()
// 	if ((S=LX(Ctl,'meta'))) {
// 		if ((Cto=this.Fo.all[S]))  M = Cto.Ctrl;  else  M = this.Fo.Ctrls[S]
// 		if (M) {
// 			Meta(M, this.Nom, LVal(Ctl));  if (Cto)  Cto.Val(LVal(M))
// 		}
// 	}
	if (this.Fo.Change)  this.Fo.Change(this)
	if (this.finChange)  this.finChange(V)
	PX(this.Ctrl,'Interactif','change')
}

CtlClass.prototype.Meta = function() {
	var Cto, Ctl, M, S, V;			//alert(this.Nom)
	Ctl = this.Ctrl
	if ((S=LX(Ctl,'meta'))) {
		if ((Cto=this.Fo.all[S]))  M = Cto.Ctrl;  else  M = this.Fo.Ctrls[S]
		if (M) {
			Meta(M, this.Nom, LVal(Ctl));  if (Cto)  Cto.Val(LVal(M))
		}
	}
}

CtlClass.prototype.setCB = function(Liste, Opt) {
	var Li, Sql, Param, S, V
	if (this.Champ)  V = this.Champ.V
	if (isObj(Liste) && Liste.Pgm) {
		Li = '&Pgm='+Liste.Pgm;  if ((S=Liste.NomT))  Li+='&NomT='+S;  if ((S=Liste.NomC))  Li+='&NomC='+S;  if ((S=Liste.Sql))  Li+='&Sql='+encodeURIComponent(S);   //alert(Li)
		Param = { Cto:this, ValDef:V, Opt:Opt }
		XmlPost(Li+AjUrl, '', this.Callback_setCB, Param)
	}else{
		Opt=Opt||{};  if (this.Champ && V)  Opt.ValDef = V
		setComboBox(this.Ctrl, Liste, Opt)
	}
}

CtlClass.prototype.Callback_setCB = function(re,Param,reJ) {
	setComboBox(Param.Cto.Ctrl, reJ, Param.Opt)
}

CtlClass.prototype.ChoixFic = function(Dest) {
	var H
	if (!Dest)  Dest='doc';  //if (Dest.substr(0,7)=='biblio/')  Dest = Dest.substr(7)
	H = "/kiwi/explor/ExplorVue.php?Doss=" + Dest + '&Fo=' + this.Fo.Nom + '&Ctl=' + this.Nom + AjUrl
	window.open(H, "", "toolbar=no,menubar=no,scrollbars=yes,resizable=yes,status=yes,width=600,height=550")
}

CtlClass.prototype.Focus = function() { try { this.Ctrl.focus() }catch(e){} }

CtlClass.prototype.noteFocus = function() {
	PX(this.Ctrl,'Interactif','focus');
	var d1, d2;  if ((d1=this.divAide) && (d2=this.Fo.divAide)) { setTimeout(function() { d2.innerHTML = d1.innerHTML;  Aff(d2) },700); }
	this.onFocus();
}
CtlClass.prototype.onFocus = function() {}

CtlClass.prototype.noteBlur = function() {  // pour régler le cas du choix d'une valeur déjà saisie dans un champ texte (onchange n'est pas appelé)
	var Ch, V, O = this.Ctrl;    //alert(Lout(O))
	var d1, d2;  if ((d1=this.divAide) && (d2=this.Fo.divAide)) { setTimeout(function() { d2.innerHTML = '';  Hide(d2) },500); }
	if (O.type=='text' && LX(O,'Interactif')=='focus') {
		PX(O,'Interactif','blur');
		Ch = this.Champ
		if (Ch) {
			if (Ch.siMaj())  this.Change();  else if ((V=LVal(O)) && Ch.V!=V)  this.Change();
		}else
			this.Change();
		this.onBlur();
	}
}
CtlClass.prototype.onBlur = function() {}

CtlClass.prototype.Val = function(V) {
	var Ch, Ctl
	Ctl = this.Ctrl
	if (V===undefined) { V = LVal(Ctl);  if (this.MsgIn && V==this.MsgIn)  V='';  return V }
	if (this.MsgIn && V==this.MsgIn)  V = ''
	if ((Ch=this.Champ)) {
		Ch.Val(V)
	}else
		PVal(Ctl,V)
}



// ====================================      BD       ===============================
var BDClass = function(Nom) {
	this.Nom = Nom
}


// ====================================      TABLE       ===============================
var TableClass = function(Nom, Proto) {
	this.Nom		= Nom
	this.NomT		= Nom
	this.NomC
	this.ChCle	= null
	this.ValCle
	this.TypCle
	this.Fo			= null
	this.all		= []
	this.nbMaj	= 0
	this.ChampClass = ChampClass
	if (Proto)  for (var K in Proto)  this[K] = Proto[K]
	if (Tables[Nom])  delete(Tables[Nom]);  Tables[Nom] = this
	return this
}

TableClass.prototype.Add = function(Nom, Typ, Taille, V, JS) {
	var Ch
	if (!JS && (JS=this.Champs))  JS=JS[Nom]
	if ((Ch=this.all[Nom])) {
		if (Typ!=undefined)  Ch.InitCh(this, Nom, Typ, Taille, V, JS)
	}else
		this.all[Nom] = new this.ChampClass(this, Nom, Typ, Taille, V, JS)
	return this.all[Nom]
}

TableClass.prototype.ReInit = function(siVal) {
	var Ch, Nom
	this.ValCle = null
	for (Nom in this.all) {
		Ch = this.all[Nom];
		if (Ch.Cto.Typ2=='meta')  Ch.ReInit(siVal)
		//if (Ch.Cto.Typ2=='meta')  alert(Lout(Ch.Cto.Ctrl))
	}
	for (Nom in this.all) {
		//if (Nom=='Data~Produit~Prix')  alert(aa)
		Ch = this.all[Nom];  if (Ch.Cto.Typ2!='meta')  Ch.ReInit(siVal)
	}
	this.finReInit(siVal)
}
TableClass.prototype.finReInit = function(siVal) {}

TableClass.prototype.ImportDataJS = function(TJS) {   // Import d'1 tableau de champ
	var Nom, V, R
	if (!TJS)  return
	for (Nom in TJS) {
		R = TJS[Nom];  V = R.V;     //alert(JSONS(R))
		switch (R.Typ) {
		  case 'N':  if (V==0)  V='';  break;
		  case 'D':  V = ConvDate(V);  break;
		}
		this.Add(Nom, R.Typ, R.Siz, V)
	}
	this.ChCle = this.all[this.NomC]
	this.initie = 1
}

TableClass.prototype.ImportRecord = function(R, Opt) {  // Import d'1 recordset R
	if (!R)  return
	if (typeof(R)=='string') { R = JSOND(R);  if (TypOf(R)=='Array')  R = R[0]; }
	var Nom, V, Ch, Cto
	if (!Opt)  Opt = {}
	for (Nom in R) {
		V = R[Nom]
		Ch = this.all[Nom]
		if (!Ch)  if (Opt.noAddCh)  continue;  else  Ch = this.Add(Nom)
		if ((Cto=Ch.Cto)) {
			switch (Cto.TypAff) {
				case 'N':  if (V==0)  V='';  break;
				case 'D':  V = ConvDate(V);  break;
				default:
					switch (Cto.Ctrl.type) {
						case "checkbox":  if (isStr(V))  V = eval(V.toLowerCase());  break;
					}
			}
		}
		Ch.Val(V)
		Ch.oldV	= Ch.V
	}
	if ((Ch=this.all[this.NomC]))		this.ValCle = Ch.Val()
	this.Fo.ValDef()
	this.finImportRecord(R, Opt)
}
TableClass.prototype.finImportRecord = function(R, Opt) {}

TableClass.prototype.ExportRecord = function() {  // Export d'1 recordset R
	var Nom, R={}, V, Ch
	for (Nom in this.all) {
		Ch = this.all[Nom]
		R[Nom] = Ch.Val()
	}
  return R
}

TableClass.prototype.Url = function(Opt) {
	// champs maj de la table + ctrl avec req ou adUrl ou commence par _. req = toujours, adUrl = seulement si champs importants modifiés
	var O, Cto, Ctl, Ch, S, JS={}, JC={}, V, Nom, NomR
	var ValCle = this.ValCle
	if (!Opt)  Opt= {};  if (typeof(Opt)!='object')  Opt = {Toujours:Opt}
	var noMaj = Opt.Toujours
	if (noMaj==-1) {
		JS[this.NomC] = ValCle
	}else{
		// Cto ayant changés
		for (Nom in this.all) {
			Ch = this.all[Nom];  V = Ch.V
			Ctl = Ch.Ctrl;  if (LX(Ctl,'meta'))  continue;    
			if ((noMaj==2 && V) || (noMaj!=2 && (V!=Ch.oldV || (!ValCle && V) || (noMaj==1 && V)))) {  // nomaj=2 pour recherche
				V = Ch.Val();  //V = Replace(V,'&','%26amp;');
				//if (ie && navV<8) { V = Replace(V,'\r','\\r');  V = Replace(V,'\n','\\n'); }
				//if (isStr(V))  V = Replace(V,'"','\\"');
				//V = encodeURIComponent(V)
				JS[Ch.Cto.NomR] = V
			}
		}
	}
	// autres champs
	if (this.Fo) {  // && !Opt.noAutresCh
		if (this.ChCle) {
			S = this.ChCle.Nom;  if (!JS[S])  JC[S] = this.ChCle.Val()
		}else{
			//alert('Aucune clé dans le formulaire');  return   : annulé pour form de recherche
		}
		for (Nom in this.Fo.all) {
			Cto = this.Fo.all[Nom];  Ctl = Cto.Ctrl;  NomR = Cto.NomR
			if (LX(Ctl,'noUrl') || LX(Ctl,'meta') || JS[NomR])  continue;		//alert(Nom+crr+Lout(Ctl))
			V = Cto.Val()
			if (LX(Ctl,'req')) {
				JS[NomR] = V
			}else{
				S = ze(LX(Ctl,'adUrl'))
				if ((Nom.substr(0,1)=='_' || S==1 || (S==2 && V))) { JC[NomR] = V }
			}
		}
		if (!ObjAsChild(JS) && !noMaj) JC={}
	}
	copyObj(JS,JC)
	return JS
}

TableClass.prototype.Sov = function(Opt) {
	// fo_CalV1.Sov({Pgm:'LoadPHP:'+PathR})
	var Fo, O, Cs, S, Param, JS, JD, DataIn
	Opt = Opt || {}
	Fo = this.Fo
	Cs = Fo.Ctrls;  if ((S=Fo.Verif())) { alert(S);  return }
	Fo.PreparSov()
	JD = this.Url(Opt.Toujours);  if (!Opt.execAjax && !ObjAsChild(JD)) { this.SovBack0('', {'parent':this, Opt:Opt});  return }  // execAjax permet de tjrs exécuter ExecAction même s'il n'y a rien à sov, mais il peut y avoir un traitement php qui suit l'action de sov
	if (JD.Action)
		JS = JD
	else if (JD._Action) {
		//JS = {Action:JD._action, BD:{NomT:this.NomT, NomC:this.NomC, ValCle:this.ValCle, DataIn:JD} }
		JS = {};  DataIn = {}
		for (S in JD) {
			if (S.substr(0,1)=='_')  JS[S.substr(1)] = JD[S];  else  DataIn[S] = JD[S];
		}
		JS.DataIn = DataIn
	}else
		JS = {Action:"Sov", BD:{NomT:this.NomT, NomC:this.NomC, ValCle:this.ValCle, DataIn:JD} };      //alert(JSW(JS));  //return
	ExecAction(JS, {parent:this, Callback:this.SovBack0})
	//S = Opt.Pgm || 'Sov';  XmlPost(Param, '/kiwi/php/Ajax.php?Action='+S+'&BugH='+Bug+AjUrl, this.SovBack0, {'parent':this, Opt:Opt} );
}

TableClass.prototype.SovBack0 = function(re, Param, reJ) {
	var This, CallB, J, T
	if (Param) { This = Param.parent;  if (Param.Opt)  CallB = Param.Opt.Callback; }    // Pour un sov de base, Fo.Table.backVal.cl1 contient la clé (Fo = This)
	if (!This)  This = this
	if (!CallB)  CallB = This.SovBack
	if (re) {
		if (Bug) { PH('DivOther',reJ);  PH('DivOther',re,1); }
		if ((T=DecoupeJS(re))) { J = T.JS || T;  This.backVal = J.backVal || J; }
	}else{
		This.backVal = { retour:'sov0' }  // pas de retour : pas champs à sauvegarder ?
	}
	CallB(re, Param, This.backVal)
}

TableClass.prototype.SovBack = function(re, Param, reJ) {   // reJ : {retour:true, ValCle:15, TypMajOk:Ajout, Msg:}
	if (re==undefined)  return;		//alert(re)
	if (this.backVal && !this.backVal.retour) { alert(this.backVal.Msg);  return }
	TraitResult(re);
}

TableClass.prototype.Suppr = function() {
	if (!this.ValCle) { alert("Rien à supprimer");  return }
	if (!confirm("D'accord pour supprimer cette fiche ?"))  return
	var JD = this.Url(-1);
	var JS = {Action:"Suppr", BD:{NomT:this.NomT, NomC:this.NomC, ValCle:this.ValCle, Relation:this.Relation, DataIn:JD} };      //alert(JSW(JS))
	ExecAction(JS, {parent:this, Callback:this.SupprBack0})
}

TableClass.prototype.SupprBack0 = function(re, Param, reJ) {
	if (Bug) { PH('DivOther',reJ);  PH('DivOther',re,1); }
	Param.parent.SupprBack(re, Param, reJ)
}

TableClass.prototype.SupprBack = function(re, Param, reJ) {
	if (re==undefined)  return;
	//var T = Decoupe(re);  if (T && T.JS && T.JS.backVal && !T.JS.backVal.retour) { alert(T.JS.backVal.Msg);  return }
}

TableClass.prototype.toJS = function() {
	var Cto, Ctl, Ch, JS, Nom, V
	JS = { NomT:this.Nom, NomC:this.NomC, ValCle:this.ValCle, R:{} }
	for (Nom in this.all) {
		Ch = this.all[Nom];  V = Ch.V
		Ctl = Ch.Ctrl;  if (LX(Ctl,'meta'))  continue
		JS.R[Nom] = Ch.Val()
	}
	return JS
}

TableClass.prototype.toString = function() {
	var Ch, H='', Nom
	H+='Nom='+this.Nom+', NomT='+this.NomT+', NomC='+this.NomC+', ValCle='+this.ValCle+crr
	for (Nom in this.all) {
		Ch = this.all[Nom]
		H+= Nom+', '+Ch.Typ
		if (Ch.Siz)  H+= ', '+Ch.Siz+cr
		H+= ', '+Ch.oldV+' = '+Ch.V+cr
	}
	return H+cr
}

// ====================================      CHAMP       ===============================
var ChampClass = function(Table, Nom, Typ, Siz, Valeur, Proto) {
	var Fo, Cto
	this.InitCh(Table, Nom, Typ, Siz, Valeur, Proto)
	this.Cto	= null
	this.Ctrl	= null
	this.Maj	= 0

	if ((Fo=this.Table.Fo)) {
		if ((Cto = Fo.all[Nom])) {
			Cto.Champ = this;  this.Cto = Cto;  this.Ctrl = Cto.Ctrl
		}
	}
}

ChampClass.prototype.InitCh = function(Table, Nom, Typ, Siz, Valeur, Proto) {
	this.Table  = Table
	this.Nom	= Nom
	this.Typ	= Typ || 'T';
	this.Siz	= Siz;
	this.V		= this.InitV(Valeur)
	this.oldV	= this.V
	if (Proto)  for (var K in Proto)  this[K] = Proto[K]
}

ChampClass.prototype.InitV = function(Valeur) {
	var V
	if (Valeur==undefined)
		V = null
	else
		switch(this.Typ) {
			case 'T': V = Valeur;  break
			case 'M': if (!(V=Valeur) && this.Table)  V = LVal(this.Table.Nom+"_"+this.Nom);  break
			case 'B': if (typeof(Valeur)=="string")  V = eval(Valeur.toLowerCase());  else  V = Valeur;  break
			default:	V = Valeur
		}
	return V
}

ChampClass.prototype.ReInit = function(siVal) {
	if (this.Nom.substr(0,1)=='_')  return
	var V = (siVal) ? this.V : ''
	this.Val(V);  this.Cto.Val(V);  this.oldV	= this.V
	//this.Cto.ValDef({sansMeta:0})   // sansMeta : dans le cas où le memo meta n'est pas encore Reinit, un champ réinitialisé reprendra la valeur du meta
	//if (this.Nom=='Data~Produit~Prix')  alert(aa)
	this.Cto.ValDef()   // sansMeta : dans le cas où le memo meta n'est pas encore Reinit, un champ réinitialisé reprendra la valeur du meta
}

ChampClass.prototype.Val = function(V) {    // Appelé par Table.Add => new ChampClass => ValDef
	var V, Ctl = this.Ctrl
	if (V===undefined) {
		if (Ctl) { V = LVal(Ctl);  if (this.Cto.MsgIn && V==this.Cto.MsgIn)  V='' }  else  V = this.V
		return V
	}
	var Cto = this.Cto;  if (Cto && Cto.MsgIn && V==Cto.MsgIn)  V = null
	this.V = V
	if (Ctl)  PVal(Ctl, V)
//if (this.Nom=='Reg1')  alert(this.Nom+crr+V+crr+this.finVal)
//if (this.Nom=='Reg1')  alert(aa)
	if (V)  this.finVal(V)
}
ChampClass.prototype.finVal = function() {
	//alert(this.Nom+crr+this.V)
}

ChampClass.prototype.siMaj = function() { var O, V;  V = this.V || '';  O = this.oldV || '';  return (V!=O) }



// ====================================      ss-PGM       ===============================
function onChange(e) {
	var Cto, S
	if ((Cto=EventCto(e)))  Cto.Change()
	if (window.onChange2)  window.onChange2(e)
	//AnnulEvent(e)
	//return false
}

function onFocus(e) { var Cto;  if ((Cto=EventCto(e)))  Cto.noteFocus(); }
function onBlur(e) { var Cto;  if ((Cto=EventCto(e)))  Cto.noteBlur(); }
function EventCto(e) { var O, S;  O = EventCtrl(e);  return O.Cto }


function FormatChamp(V,Typ,TypAff,Opt) {
	var O, V, C, Test, p
	if (!Opt)  Opt = {}
	if (typeof(V)=='string') { C = V.substr(0,1);  if (C=='=')  V = V.substr(1) }
	if (!V)  return C+V
	if (TypAff=='N')  Typ = 'N'
		//alert(Typ+cr+TypAff+cr+V)
	switch (Typ) {
		case 'N':
			V = Replace(V,",",".");  S = parseFloat(V);
			if (S!=V) { if (isNaN(S)) S='';  if (V) Opt.MsgErr = V + " n'est pas un nombre. Conversion automatique en '" + S + "'";  V=S }
			break
	}
	switch (TypAff) {
		case 'D':  V = ConvDate(V);  break
		case 'email':
			Test = new RegExp("^([a-zA-Z0-9_-])+([.]?[a-zA-Z0-9_-]{1,})*@([a-zA-Z0-9-_]{2,}[.])+[a-zA-Z]{2,3}$");
			if (!Test.exec(V))  Opt.Msg = "Adresse invalide (format : nom@domaine.dom)"
		  break
		case 'minus':  if (C!="=")  V = V.toLowerCase();  break
		case 'majus':  if (C!="=")  V = V.toUpperCase();  break
		case 'minus2':  V = Minus(V);  break
		case 'majus2':  V = Majus(V);  break
		case 'NP':  	 if (C!='=')  V = NomPropre(V);  		break
		case 'tel':
			V = ClearText (V, "!isNaN(Car) || Car=='.'");  if (V.length!=10)  break
			V = V.substr(0,2) + "." + V.substr(2,2) + "." + V.substr(4,2) + "." + V.substr(6,2) + "." + V.substr(8,2)
			break
	}
	return V
}

function Verif_IP(V) {
	var M='', reg = /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/ ;
		//\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b
	if (V.match(reg))  M = "L'adresse ip " + V + " n'est pas valide";  return M
}
function Verif_Date(V) {    //alert(Verif_Date("12/01/2003 10:08"))
	var M='', reg, f1, f2, f3
	f1 = "[0-9]{2}[/]{1}[0-9]{2}[/]{1}[0-9]{4}";  f2 = "[ ][0-9]{2}[:]{1}[0-9]{2}";  f3 = "[ ][0-9]{2}[:]{1}[0-9]{2}[:]{1}[0-9]{2}"
	reg = new RegExp("^"+f1+"$","g");
	if (!reg.test(V)) {
		reg = new RegExp("^"+f1+f2+"$","g");
		if (!reg.test(V)) { reg = new RegExp("^"+f1+f3+"$","g");  if (!reg.test(V))  M = V + " n'est pas au format date (ex : 15/02/2010)" }
	}
	return M
}
function Verif_eMail(V) { if (!V)  return;  var M='', reg = new RegExp("^([a-zA-Z0-9_-])+([.]?[a-zA-Z0-9_-]{1,})*@([a-zA-Z0-9-_]{2,}[.])+[a-zA-Z]{2,3}$");  if (!reg.exec(V))  M = V + " n'est pas une e-mail valide";  return M }

function Meta(Ctl, Nom, V) {
	var Cto, S, J, JVar, e, Pg, T, i, L, c, O
	O = LID(Ctl)
	if (!(S=LVal(O)))
		J = {}
	else{
		S = Replace(S,'\r\n','\\n');  S = Replace(S,'\n','\\n');
		J = JSOND(S);  if (!J)  return;     //alert(Nom+crr+S+crr+JSONS(J))
	}
	JVar = J
	if (Nom.indexOf('~')>=0) {
		T = Nom.split('~');  L = T.length - 1
		for (i=0; i<L; i++) {
			Nom = T[i];  if (!JVar[Nom])  JVar[Nom] = {};  JVar = JVar[Nom];
		}
		Nom = T[L]
	}
	if (V===undefined) { V = JVar[Nom];  if (typeof(V)=='object')  V = JSONS(V);  return V }
	if (typeof(V)=='string') { c = (V+'').substr(0,1);  if (c=='{' || c=='[')  V = JSOND(V) }
	if ((Pg=window['debMaj_'+LX(O,'name')])) {
		Opt = { Nom:Nom, V:V }
		if (!(e=Pg(JVar,Opt)))  return
		Nom = Opt.Nom;  V = Opt.V
		//alert(Nom+cr+V+crr+JSW(J))
	}
	if (V)  JVar[Nom] = V;  else  delete(JVar[Nom])
	S = JSONS(J);  if (S=='{}' || S=='[]')  S=''
	PVal(O,S)
  //alert(S+crr+JSONS(J))
}

function SelectOpt(Ctl) {
	Ctl = LID(Ctl);  if (!Ctl)  return
	if (!Ctl.options || Ctl.selectedIndex<0)  return null
  return Ctl.options[Ctl.selectedIndex];
}

function setComboBox(CB, Liste, Opt) {
	var Cod, CodV, S, O, V, N, i, j, ValDef, R, okSel, nb
	CB = LID(CB);  if (!CB)  return
	if (typeof Liste=='string') {
		switch(Liste) {
			case 'SelDat':  Liste = ",(Tous);-0 day,Aujourd'hui;-2 day,3 derniers jours;-6 day,7 derniers jours;-1 month,30 derniers jours;-3 month,3 derniers mois;-12 month,12 mois glissants;;M-1,Mois précédent;M,Mois en cours;A,Année en cours";  break;
		}
		Liste = Tableau2D(Liste);		//alert(JSW(Liste))
	}
	if (Opt==undefined)  Opt={};  else if (!isObj(Opt))  Opt = {Clear:Opt}
	if (Opt.Clear) { delete(Opt.Clear);  CB.innerHTML='' }
	if ((S=LX(CB,'AjDeb'))) { Opt.AjDeb = Tableau2D(S);  RX(CB,'AjDeb') }
	if (Opt.AjDeb) {
		//Opt.AjDeb = TdelDoublons(Opt.AjDeb,Liste);  >> délicat sur des clés numériques
		setComboBox(CB, Opt.AjDeb);
	}
	ValDef = Opt.ValDef;  j=0

	for (Cod in Liste) {
		if (Cod=='length')  continue
		R = Liste[Cod];
		if (Opt.Test && !eval(Opt.Test))  continue
		if (Opt.Sauf && Tin(Opt.Sauf,Cod))  continue
		CodV = Cod
		O = document.createElement('OPTION')
		if (Opt.Typ=='obj') {
			V = R[Opt.Lib]
		}else if (typeof(R)=='object') {      // tableaux T[n]={Cle,Libellé}
			i = 0
			for (N in R) {
				S = R[N]
				if (i==0)  CodV = S
				else if (i==1)  V = S
				else{
					PX(O,N,S)
				}
				i++
			}
			if (i<2) { V=CodV;  CodV=Cod  }  // cas des tableaux T[Cle]=Libellé
		}else{
			V = R
		}
		O.value = CodV;  O.text = V
		if (Opt.Sel)  if ((Opt.Sel=='_1_' && j==0) || Opt.Sel==CodV)  O.selected=true;
		CB.add(O,Opt.pos)
		j++
	}
	nb = CB.options.length
	if (nb==1)  ValDef = CodV
	if (Opt.AjFin)  setComboBox(CB, Opt.AjFin)
	if (ValDef!==undefined)  PVal(CB,ValDef)
}

function Edit_to_Memo() {
	var n, id, inst, S, Ctl, Cto, tinyMCE = App.tinyMCE
	if (!tinyMCE)  return
	tinyMCE.triggerSave();
	for (id in tinyMCE.editors) {
		Ctl = LID(id);  if ((Cto=Ctl.Cto)) { Cto.Val(LVal(Ctl));  Cto.Change() }
    //inst = tinyMCE.editors[id];  alert(inst.isNotDirty+crr+inst.getContent())
  }
}

PutValSql = function(Chaine, D, V) {  //Chaine = contact;CodCo;Prenom,Nom, D = où placer le résultat, V = code à rechercher
	D = LID(D);  if (!Chaine || D.Code==V)  return
	D.Code = V
	var S, Sql, t
	if (Isole(Chaine,' ',1)=='SELECT') {
		Sql = Replace(Chaine,'$V',V)
	}else{
		t = Chaine.split(';')
		S = 'CONCAT('+Replace(t[2],',',",' ',")+')'
		Sql = "SELECT "+S+" AS Rep FROM "+t[0]+" WHERE "+t[1]+"="+V
	}
		//alert(Sql)
	ExecAction([{Action:'LitSql',Sql:Sql}], {Callback:function(re){
		var JS = JSOND(re); 		//alert(JSW(JS))
		if (JS) {
			JS=JS[0];  D.innerHTML = JS.Rep
		}
	} })
}

