function Table(nodeOrIdNode) {
    if (!Table.Instances) Table.Instances = new Array();
    Table.Instances[nodeOrIdNode] = this;

    this._name = "";
    if ((typeof nodeOrIdNode)=="string") {
	this._name = nodeOrIdNode;
	this._tableNode = document.getElementById(nodeOrIdNode);
	if (this._tableNode==null) {
	    alert("L'élément "+nodeOrIdNode+" qui devrait être un id d'une balise <table> n'existe pas");
	    return;
	}
    } else this._tableNode = nodeOrIdNode;

    var tbodys = this._tableNode.getElementsByTagName('tbody');
    if (tbodys.length>0) this._tbodyNode = tbodys[0];
    else {
	this._tbodyNode = document.createElement('tbody');
	this._tableNode.appendChild(this._tbodyNode);
    }

    this._lang = 'fr';

    this._nodeTrHeader = null;
    this._ths = new Array();
    this._thOrdered = null;

    this._rowButtons = new Array();

    this._trs = new Array();

    this._addEditButton = false;
    this._addDeleteButton = false;
}
Table.prototype.setLanguage = function(lang) {
    this._lang = lang;
}
Table.prototype.addEditAndDeleteButtons = function(bool) {
    this._addEditButton = bool;
    this._addDeleteButton = bool;
}
Table.prototype.addEditButton = function(bool) {
    this._addEditButton = bool;
}
Table.prototype.addDeleteButton = function(bool) {
    this._addDeleteButton = bool;
}
Table.prototype.numRows = function() { return this._trs.length; }
Table.prototype.node = function() { return this._tableNode; }

function RowButton() {}
Table.prototype.addRowButton = function(button) {
    this._rowButtons.push(button);
}
Table.prototype.createHeader = function() {
    this._nodeTrHeader = document.createElement('tr');
    this._tbodyNode.appendChild(this._nodeTrHeader);
    for (var i=0;i<this._ths.length;i++) {
	var th = this._ths[i];
	th.create();
	this._nodeTrHeader.appendChild(th.node());
    }
    if (this._addEditButton ||
	this._addDeleteButton || 
	this._rowButtons.length>0) {
	var thNode = document.createElement('th');
	thNode.innerHTML = "&nbsp;";
	this._nodeTrHeader.appendChild(thNode);
    }
}
Table.prototype.createRows = function(rows) {
    if (rows==null) return;

    for (var i=0;i<rows.length;i++) {
	var row = rows[i];
	if (row['id']==null) {
	    alert("impossible de charger les données : le champ id n'est pas présent");
	    return;
	}
	this.addRow(row, false);
    }
}
Table.prototype.deleteRows = function() {
    for (var i=0;i<this._trs.length;i++) {
	var tr = this._trs[i];
	tr.node().parentNode.removeChild(tr.node());
    }
    this._trs = new Array();
}
Table.prototype.addRow = function(row, reorder) {
    var tr = new TR(this, row);
    this._tbodyNode.appendChild(tr.node());
    this._trs.push(tr);

    for (var i=0;i<this._ths.length;i++) {
	var th = this._ths[i];
	
	var data = "";
	if (row['datas'][th.index()]!=null) data = row['datas'][th.index()];

	var td = new TD(th, tr, data);
    }

    if (this._addEditButton || 
	this._addDeleteButton ||
	this._rowButtons.length>0) {
	var tdNode = tr.addTDNode();
	if (this._addEditButton) tr.addEditButton(tdNode);
	if (this._addDeleteButton) tr.addDeleteButton(tdNode);
	for (var i=0;i<this._rowButtons.length;i++) {
	    var button = this._rowButtons[i];
	    var node = button.create(tr);
	    if (node!=null) tdNode.appendChild(node);
	} 
    }

    if (arguments.length<2 || arguments[1]) this._reorder();
}
Table.prototype.modRow = function(row) {
    var tr = this.trFromId(row['id']);
    tr.mod(row['datas']);
    this._reorder();
}
Table.prototype.order = function(index, toggle) {
    var th = this.thFromIndex(index);
    if (th==null) return;

    var asc = th.ascOrdered();
    if (toggle) asc = !asc;

    if (asc) this.orderAsc(index);
    else this.orderDesc(index);
}
Table.prototype.orderAsc = function(index) {
    var th = this.thFromIndex(index);
    if (th==null) return;
    if (this._thOrdered==th && th.ascOrdered()) return;

    if (this._thOrdered!=null) this._thOrdered.hideOrdering();

    th.orderAsc();
    th.showOrdering();

    this._thOrdered = th;
    this._reorder();
}
Table.prototype.orderDesc = function(index) {
    var th = this.thFromIndex(index);
    if (th==null) return;
    if (this._thOrdered==th && !th.ascOrdered()) return;

    if (this._thOrdered!=null) this._thOrdered.hideOrdering();

    th.orderDesc();
    th.showOrdering();

    this._thOrdered = th;
    this._reorder();
}
Table.prototype._reorder = function() {
    if (this._thOrdered==null) return;

    var s = this;
    var func = function(row1, row2) { // retourne plus grand que 0, alors row1 > row2
	var iCol = s._thOrdered.iCol();
	var asc = s._thOrdered.ascOrdered();
	var dataType = s._thOrdered.dataType();
	
	var data1 = row1.data(iCol);
	var data2 = row2.data(iCol);
	
	var res = -1;	
	if (asc) res = 1;
	if (dataType=="alpha") {
	    data1 = data1.toLowerCase();
	    data2 = data2.toLowerCase();
	    if (data1==data2) return 0;
	    var array = new Array(data1, data2);
	    array.sort();
	    if (array[0]==data1) res = -res;

	} else if (dataType=="num") {
	    if (data1==data2) return 0;
	    if (data1<data2) res = -res;

	} else if (dataType=="date") {
	    if (data1.getTime()<data2.getTime()) res = -res;
	}

	return res;
    }
    this._trs.sort(func);
    for (var i=0;i<this._trs.length;i++) {
	var trNode = this._trs[i].node();
	trNode.parentNode.appendChild(trNode);
    }
}

Table.prototype.thFromIndex = function(index) {
    for (var i=0;i<this._ths.length;i++) if (this._ths[i].index()==index) return this._ths[i];
    return null;
}
Table.prototype.trFromId = function(id) {
    for (var i=0;i<this._trs.length;i++) if (this._trs[i].id()==id) return this._trs[i];
    return null;
}

Table.prototype.hide = function() {
    this._tableNode.style.display = "none";
}
Table.prototype.show = function() {
    this._tableNode.style.display = "block";
}
Table.prototype.addTH = function(th) {
    th._table = this;
    th._iCol = this._ths.length;
    this._ths.push(th);
}

Table.prototype.onRemoveClicked = function(tr) {
    if (!confirm('Supprimer cette ligne ?')) return;
    if (!this.onRemove) {
	alert("La méthode onRemove n'est pas définie...");
	return;
    }

    if (!this.onRemove(tr.id())) return;

    for (var i=0;i<this._trs.length;i++) {
	if (this._trs[i]==tr) {
	    this._trs.splice(i, 1);
	    break;
	}
    }    
    tr.node().parentNode.removeChild(tr.node());
}
Table.prototype.removeId = function(id) {
    var tr = null;
    for (var i=0;i<this._trs.length;i++) {
	if (this._trs[i].id()==id) {
	    tr = this._trs[i];
	    this._trs.splice(i, 1);
	    break;
	}
    }    
    if (tr!=null) tr.node().parentNode.removeChild(tr.node());
}
Table.prototype.onEditClicked = function(tr) {
    if (!this.onEdit) {
	alert("La méthode onEdit n'est pas définie...");
	return;
    }
    this.onEdit(tr.id(), tr);
}

function TH(index, dataType, intitule) {
    this._table = null;
    this._node = document.createElement('th');

    this._index = index;
    this._dataType = dataType;
    this._intitule = intitule;

    this._iCol = 0; // renseigné lorsqu'ajouté au tableau
    
    this._ordering = false;
    this._ascOrdered = true;

    this.onDblClick = null; // Fonction lorsqu'une cellule de cette colonne reçoit un double click
}
TH.prototype.setOrdering = function(bool) { this._ordering = bool; }

TH.prototype.node = function() { return this._node; }
TH.prototype.index = function() { return this._index; }
TH.prototype.iCol = function() { return this._iCol; }
TH.prototype.dataType = function() { return this._dataType; }
TH.prototype.ascOrdered = function() { return this._ascOrdered; }
TH.prototype.create = function() { 
    if (!this._ordering) this._node.innerHTML = this._intitule;
    else {
	var a = document.createElement('a');
	a.href = "#";
	var table = this._table;
	var s = this;
	var index = this._index;
	a.onclick = function() { table.order(index, false); return false; }
	a.innerHTML = this._intitule;
	this._node.appendChild(a);
	
	a = document.createElement('a');
	a.href = "#";
	a.onclick = function() { table.order(index, true); return false; }
	
	this._imgOrder = document.createElement('img');
	this._imgOrder.style.display = "none";
	this._imgOrder.src = "css/images/up.gif";
	a.appendChild(this._imgOrder);
	
	this._node.appendChild(a);	
    }
}
TH.prototype.orderAsc = function() {
    if (!this._ordering) return;
    this._ascOrdered = true;
    this._imgOrder.src = "css/images/down.gif";
}
TH.prototype.orderDesc = function() {
    if (!this._ordering) return;
    this._ascOrdered = false;
    this._imgOrder.src = "css/images/up.gif";
}

TH.prototype.hideOrdering = function() {
    if (!this._ordering) return;
    this._imgOrder.style.display = "none";
}
TH.prototype.showOrdering = function() {
    if (!this._ordering) return;
    this._imgOrder.style.display = "block";
}

function TR(table, row) {
    this._table = table;
    this._id = row['id'];
    this._node = document.createElement('tr');
    this._tds = new Array();
    this._row = row['datas']; // Les données de la ligne
}
TR.prototype.node = function() { return this._node; }
TR.prototype.id = function() { return this._id; }
TR.prototype.setClassName = function(name) { this._node.className = name; }

TR.prototype.addTDNode = function() { 
    var tdNode = document.createElement('td');
    this._node.appendChild(tdNode);
    return tdNode;
}
TR.prototype.addEditButton = function(tdNode) { 
    var a = document.createElement('a');
    a.href = "#";
    a.style.marginRight = "15px";
    var tr = this;
    var table = this._table;
    a.onclick = function() {
	table.onEditClicked(tr);
	return false;
    }
    a.innerHTML = '<img src="css/images/edit.gif" />';
    if (this._table._lang=='fr') a.title = "Editer cette entrée";
    else a.title = "Edit this entry";
    tdNode.appendChild(a);
}
TR.prototype.addDeleteButton = function(tdNode) { 
    var a = document.createElement('a');
    a.href = "#";
    a.style.marginRight = "15px";
    var tr = this;
    var table = this._table;
    a.onclick = function() {
	table.onRemoveClicked(tr);
	return false;
    }
    a.innerHTML = '<img src="css/images/delete.gif" />';
    if (this._table._lang=='fr') a.title = "Supprimer cette entrée";
    else a.title = "Remove this entry";
    tdNode.appendChild(a);
}
TR.prototype.addTD = function(td) { this._tds.push(td); }
TR.prototype.data = function(iCol) { 
    return this._tds[iCol].data(); 
}
TR.prototype.dataOfIndex = function(index) { 
    for (var i=0;i<this._tds.length;i++) {
	var td = this._tds[i];
	if (td.index()==index) return td.data();
    }
    return "";
}
TR.prototype.td = function(iCol) { 
    return this._tds[iCol]; 
}
TR.prototype.tdOfIndex = function(index) { 
    for (var i=0;i<this._tds.length;i++) {
	var td = this._tds[i];
	if (td.index()==index) return td;
    }
    return null;
}
TR.prototype.mod = function(row) {
    // Mettre à jour this._row avec toutes les données présentes dans row
    for (var key in row) if (this._row[key]!=null) this._row[key] = row[key];

    for (var i=0;i<this._tds.length;i++) {
	var index = this._tds[i].index();
	if (row[index]!=null) this._tds[i].setData(row[index]);
    }
}

function TD(th, tr, data) {
    this._th = th;
    this._tr = tr;
    this._node = document.createElement('td');

    tr.addTD(this);
    this.setData(data);
    tr.node().appendChild(this._node);
}
TD.prototype.index = function() { return this._th.index(); }
TD.prototype.data = function() { return this._data; }
TD.prototype.node = function() { return this._node; }
TD.prototype.setData = function(data) { 
    var type = typeof data;
    if (this._th.dataType()=='num' && type=="string") data = parseFloat(data, 10);

    var s = this;
    if (!this._th.setInnerHTML) {
	if (type=="string" && data=="") this._node.innerHTML = "&nbsp;";
	else {
	    if (!this._th.onDblClick) this._node.innerHTML = data;
	    else {
		var a = document.createElement('a');
		a.href = "#";
		a.onclick = function() { return false; }
		a.ondblclick = function() {
		    s._th.onDblClick(s);
		}
		a.innerHTML = data;
		this._node.innerHTML = "";
		this._node.appendChild(a);
	    } 
	}
	this._data = data;

    } else {
	var a = this._th.setInnerHTML(data, this._tr._row, this._tr.id(), this._tr);
	if (a['html']!=null) this._node.innerHTML = a['html'];
	else if (a['node']!=null) {
	    this._node.innerHTML = "";
	    this._node.appendChild(a['node']);	

	} else alert("setInnerHTML doit retourner un tableau avec un index html ou node");
	
	if (a['data']==null) this._data = data;
	else this._data = a['data'];
    }
}


