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, Fo
	F = ElemPAt(btn,'ojs');  Fo = eval(LX(F,'ojs'));  if (!Fo)  return;    //alert(Fo.Nom+crr+Lout(F))
	if (Pgm)  Fo[Pgm]()
	return Fo
}

var getFo = function(btn) { var F = ElemPAt(btn,'ojs');  return eval(LX(F,'ojs')) }


// ====================================      FO       ===============================
var FoClass = function(Nom, id, Table, Opt) {
  var Fo
	if (!Opt)  Opt={}
	id = id || Nom;
	if (typeof(id)=='string')  Fo = LID(id);  else  Fo = id;  if (!Fo)  return
	if (!(this.id=Fo.name))  this.id = Fo.id
	if (typeof(Nom)!='string')  Nom = this.id
	window['fo_'+Nom] = this;  PX(Fo,'ojs','fo_'+Nom)

	this.Nom		= Nom
	this.Fo			= Fo
	this.Div		= Fo
	this.isForm = (this.Fo.tagName=='FORM')
	this.initie = false
	if (Table)
		this.TableAutoCre = false
	else{
		Table = new TableClass(Nom)
		this.TableAutoCre = true
	}
	this.Table	= Table;  if (Table)  Table.Fo = this
	this.Opt		= Opt
	this.all		= []
	this.Typfo  = Opt.Typfo
  this.CtlMaj = '_ChampsMaj'
	this.CtlClass = Opt.CtlClass || CtlClass
	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.Fo.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 (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
		if (Tag=='SELECT') {
			if ((S=LX(Ctl,'ActionSql'))) {
				Cto.setCB(JST(S))
			}else if (this.Opt.Selects && (O1=this.Opt.Selects[Nom])) {
				setComboBox(Ctl, O1, {ValDef:null,AjDeb:{'':'(?)'}})
				Cto.ValDef()
			}else{
				if ((O1=this.getCtl(LX(Ctl,'Source')))) { Hide(Ctl,'DIV');  Aff(O1,'DIV'); }
			}
		}
		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.getCtl('_NomC')))		T.NomC = 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()
	}
	if (this.FinInit)  this.FinInit()
	//alert(this+crr+this.Table)
}

FoClass.prototype.Destroy = function(Div) {  // Détruit les Cto et Ctrl de la Div mais pas les Table.Ch
	var T, Ctl, 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 ((S=LX(Ctl,'ojs')) && (Cto=eval(S))) {
			Cto.Destroy();  delete(this.all[Cto.id])
		}
	}
}

FoClass.prototype.Add = function(Nom, id) {
	if (!id)  id = Nom
	var Ch = new this.CtlClass(this, Nom, id)
	if (!Ch.id)  Ch = this.all[Nom];  else  this.all[Nom] = Ch
	return Ch
}

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.Changements = function() {
	var O, re, S
	if (this.Table)  re = this.Table.Changements();   //alert(re)
	//wb('-------\n'+re+'\n')
	if ((O=this.all[this.CtlMaj]))  O.Val(re)
  return re
}

FoClass.prototype.gereAffich = function() {
	var Cto, Ctl, D, L, sp, Nom
	for (Nom in this.all) {
		Cto  = this.all[Nom];  Ctl = Cto.Ctrl;
		D = Ctl.parentNode
		L = LTags(D,'LABEL',0)
		if (L) {
			sp = ElemIn(L, "@C='Etoile'", true, 0);
			if (Cto.NonVide) {
				if (!sp)  InsertHtml("<span class=Etoile>&nbsp;*</span>", L, '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, Ch, 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.id			= id
		this.Fo			= Fo
		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
	}
	PX(Ctl,'ojs','fo_'+Fo.Nom+".all['"+Nom+"']")
	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.Init = function() {
	var Fo, Ctl, O, M, S
	Fo  = this.Fo;  Ctl = this.Ctrl
}

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
					}
					XmlPost2('&Pgm=LitSql&NomC='+NomC+'&Sql='+encodeURIComponent(Sql)+AjUrl, '', Pgm, Param)
				}
			}
		}
	}
}

CtlClass.prototype.Change = function() {
	var Cto, Ctl, M, S, V;      //alert(Lout(this.Ctrl))
	Ctl = this.Ctrl
	PVal(Ctl,Trim(LVal(Ctl)))
	V = this.Format()
	this.Val(V)
	if ((S=LX(Ctl,'ChSuit'))) {
		if ((S=LX(S,'ojs'))) {
			Cto = eval(S);  Cto.Traite()
		}
	}
	if ((S=LX(Ctl,'Source'))) {
		if ((S=LX(S,'ojs'))) {
			Cto = eval(S);  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)
	this.Fo.Changements()
	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) {
	if (isObj(Liste) && Liste.Pgm) {
		var Li, Sql, Param, S
		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 }
		XmlPost2(Li+AjUrl, '', this.Callback_setCB, Param)
	}else{
		setComboBox(this.Ctrl, Liste, Opt)
	}
}

CtlClass.prototype.Callback_setCB = function(re, Param) {
	var JS = JSOND(re)
	setComboBox(Param.Cto.Ctrl, JS)
}

CtlClass.prototype.ChoixFic = function(Dest) {
	var H
	if (!Dest)  Dest='doc';  //if (Dest.substr(0,7)=='biblio/')  Dest = Dest.substr(7)
	H = "/SIP/explorer/ExplorVue.php?Doss=" + Dest + '&Fo=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');  if (this.Fo.debFocus)  this.Fo.debFocus(this);  this.onFocus();  if (this.Fo.finFocus)  this.Fo.finFocus(this) }
CtlClass.prototype.noteFocus = function() { PX(this.Ctrl,'Interactif','focus');  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))
	if ((O.type=='text' || O.type=='password') && 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)
	//if (V)  this.finVal(V)
}

CtlClass.prototype.ValSql = function() { var Ch;  if ((Ch=this.Champ))  return Ch.ValSql();  else  return this.Val() }



// ====================================      BD       ===============================
var BDClass = function(Nom) {
	this.Nom = Nom
}


// ====================================      TABLE       ===============================
var TableClass = function(Nom, Cle, Typ, ValCle, Opt) {
	if (!Opt)  Opt={}
	this.Nom		= Nom
	this.ChCle	= null
	this.NomC		= Cle
	this.TypCle	= Typ
	this.ValCle	= ValCle
	this.Fo			= null
	this.all		= []
	this.nbMaj	= 0
	this.ChampClass = Opt.ChampClass || ChampClass
	if (Tables[Nom])  delete(Tables[Nom]);  Tables[Nom] = this
	return this
}

TableClass.prototype.Add = function(Nom, Typ, Taille, V, Opt) {
	var Ch
	if ((Ch=this.all[Nom])) {
		if (Typ!=undefined)  Ch.InitCh(this, Nom, Typ, Taille, V)
	}else
		this.all[Nom] = new this.ChampClass(this, Nom, Typ, Taille, V, Opt)
	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)
	}
	//if ((Ch=this.Fo.all[this.Fo.CtlMaj]))  Ch.Champ.Val('')
	this.finReInit()
}
TableClass.prototype.finReInit = function() {}

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.Changements = function() {
	var O, re='', V, O, Li='', Nom, Ch
	//wb('!\n')
	for (Nom in this.all) {
		Ch = this.all[Nom];  if (Nom==this.Fo.CtlMaj || (Ch.Cto && Ch.Cto.bd==1))  continue
		V = Ch.V || '';  O = Ch.oldV || '';  //Li+=Nom + ' = '+Ch.V+', '+Ch.oldV+cr
		if (V!=O) {
			re+=';'+Nom;			//wb(Nom+'='+Ch.V+','+Ch.oldV+'\n')
		}
	}
  //alert(Li)
	if (re)  re = re+';'
	return re
}

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, re='', rc='', ms='', V, Nom, NomR
	var ValCle = this.ValCle
	if (!Opt)  Opt= {};  if (typeof(Opt)!='object')  Opt = {Toujours:Opt}
	var noMaj = Opt.Toujours
	if (noMaj==-1)
		re = '&'+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;    //ms+= Nom+','+V+','+Ch.oldV+'\n'
			if (V!=Ch.oldV || (!ValCle && V) || (noMaj==1 && V)) {
				V = Ch.ValSql();  //V = Replace(V,'&','%26amp;');
				V = encodeURIComponent(V)
				re+='&'+Ch.Cto.NomR+'='+V
			}
		}
	}
  	//alert(re+crr+'->'+ms)
	// autres champs
	if (this.Fo) {  // && !Opt.noAutresCh
		if (this.ChCle) {
			S = this.ChCle.Nom;  if (re.indexOf('&'+S+'=')<0)  rc+='&'+S+'='+this.ChCle.Val()
		}else{
			//alert('Aucune clé dans le formulaire');  return   : annulé pour form de recherche
		}
		for (Nom in this.Fo.all) {
//if (Nom=='_NomT') alert(aa)
			Cto = this.Fo.all[Nom];  Ctl = Cto.Ctrl;  NomR = Cto.NomR
			if (LX(Ctl,'noUrl') || LX(Ctl,'meta') || re.indexOf('&'+NomR+'=')>=0)  continue;		//alert(Nom+crr+Lout(Ctl))
			V = Cto.ValSql()
			if (LX(Ctl,'req'))
				re+='&'+NomR+'='+V
			else{
				S = ze(LX(Ctl,'adUrl'))
				if ((Nom.substr(0,1)=='_' || S==1 || (S==2 && V))) { rc+='&'+NomR+'='+V }
			}
		}
    //alert(re+crr+rc)
		if (!re && !noMaj)  rc=''
	}
	re = AddStr(re,rc,'&')
	return re
}

TableClass.prototype.Sov = function(Opt) {
	// fo_CalV1.Sov({Pgm:'LoadPHP:'+PathR})
	var Fo, O, Cs, S, Param
	Opt = Opt || {}

	//var Param = this.Url(Opt.Toujours);

	Fo = this.Fo
	Cs = Fo.Ctrls;  if ((S=Fo.Verif())) { alert(S);  return }
	Fo.PreparSov()
	Fo.Changements()
	var Param = this.Url(Opt.Toujours);  if (!Param) { this.SovBack0('', {'parent':this, Opt:Opt});  return }
	Param+=AjUrl
	//alert(Param+crr+this);  //wb(Param+'\n');  return
	//Param = location + '?' + Param;  window.open(Param);  return
	if (this.Fo && (O=Cs['_action'])) {
	  ExecAction(O.value, Param)
	  this.SovBack0(null,{Opt:Opt})
		return
	}
	S = Opt.Pgm || 'Sov:'+Fo.id
	XmlPost2(Param, '/SIP/iview/appli.php?Action='+S+'&BugH='+Bug+AjUrl, this.SovBack0, {'parent':this, Opt:Opt} );
}

TableClass.prototype.SovBack0 = function(re, Param) {
	var This, CallB
	if (Param) { This = Param.parent;  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)  LID('DivOther').innerHTML = re
		var T = Decoupe(re);  if (T && T.JS && T.JS.backVal) { This.backVal = T.JS.backVal; }
	}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 Param = this.Url(-1);
	Param+=AjUrl
	//alert(Param);  return
	XmlPost2(Param, '/SIP/iview/appli.php?Action=Suppr'+'&BugH='+Bug+AjUrl, this.SupprBack0, {'parent':this} );
}

TableClass.prototype.SupprBack0 = function(re, Param) {
	if (Bug)  LID('DivOther').innerHTML = re
	Param.parent.SupprBack(re, Param)
}

TableClass.prototype.SupprBack = function(re, Param) {
	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, 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+', 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, Opt) {
	var Fo, Cto
	this.InitCh(Table, Nom, Typ, Siz, Valeur)
	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) {
	this.Table  = Table
	this.Nom	= Nom
	this.Typ	= Typ || 'T';
	this.Siz	= Siz;
	this.V		= this.InitV(Valeur)
	this.oldV	= this.V
}

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) {
	var V, Ctl = this.Ctrl
	if (V==undefined) {
		if (Ctl)  return LVal(Ctl);  else  return this.V
	}
	var Cto = this.Cto;  if (Cto && Cto.MsgIn && V==Cto.MsgIn)  V = null
	this.V = V
	if (Ctl)  PVal(Ctl, V)
	//if (Cto)  Cto.Fo.Changements()
	if (V)  this.finVal(V)
}
ChampClass.prototype.finVal = function() {
	//alert(this.Nom+crr+this.V)
}

ChampClass.prototype.ValSql = function() {
	var Cto, V
	V = this.Val()
	if ((Cto=this.Cto)) {
		switch(Cto.TypAff) {
			case 'tel': V = ClearText(V, "!isNaN(C) && C!='.' && C!=' '");  break
		}
	}
	return V
}

ChampClass.prototype.siMaj = function() { var O, V;  V = this.V || '';  O = this.oldV || '';  return (V!=O) }



// ====================================      ss-PGM       ===============================
function onChange(e) {
	var O, Cto, S
  if ((Cto=EventCto(e)))  Cto.Change()
  if (window.onChange2)  window.onChange2(e)
}

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);  if ((S=LX(O,'ojs')))  return Eval(S) }


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(C) && C!='.' && C!=' '")
			O = V.match(/([0-9]{1,2})/g);  V = O.join(' ')
			if (V.substr(0,2)=='00')  V = '00'+V.substr(3)
			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 CB, 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))
	}
	Opt = 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);  Cto = eval(LX(Ctl,'ojs'));  Cto.Val(LVal(Ctl));  Cto.Change()
    //inst = tinyMCE.editors[id];  alert(inst.isNotDirty+crr+inst.getContent())
  }
}

