function Widget(parent, node) {
    if (arguments.length==0) return;

    this.superClass = ObjectHandler;
    this.superClass();
    delete this.superclass;

    this._parentWidget = parent;
    this._nodeWidget = node;

    this._styleDisplayToBeVisibleWidget = "block";
    var nodeName = node.nodeName.toLowerCase();
    if (nodeName=='table' ||
	nodeName=='tbody' ||
	nodeName=='tr' ||
	nodeName=='th' ||
	nodeName=='td') this._styleDisplayToBeVisibleWidget = "";

    /**
     * Si est true, alors doit être visible, mais seulement si son parent est visible
     */
    this._visibleWidget = false;

    /**
     * Est true seulement si est réellement visible, c'est à dire que tous ses parents sont visibles
     * Si est false, this._visibleWidget peut être à true, indiquant que si son parent devient visible,
     * alors ce widget doit être également visible
     */
    this._reallyVisibleWidget = false;

    /**
     * 0 : Ce widget ne gère pas les dimensions de ses enfants
     * 1 : les enfants de ce widget restent dans le flux, c'est donc un layout vertical
     * 2 : les enfants de ce widget doivent se placer sur une ligne, c'est donc un layout horizontal
     *     excepté le premier enfant, tous les autres seront mis en position: absolute
     */
    this._layoutWidget = 0;

    if (parent==null && node==document.body) {
	this._visibleWidget = false;
	this._reallyVisibleWidget = false;
	document.body.style.display = "none";

    } else if (parent==null) {
	this._parentWidget = Widget.mainWidget();
	node.style.display = "none";
	this._parentWidget.addChildWidget(this);

    } else {
	node.style.display = "none";    
	this._parentWidget.addChildWidget(this);
    }

    this._paddingsAndBordersWidth = this.paddingWidget('left') + this.paddingWidget('right');
    this._paddingsAndBordersWidth += this.borderWidget('left')+ this.borderWidget('right');

    this._paddingsAndBordersHeight = this.paddingWidget('top') + this.paddingWidget('bottom');
    this._paddingsAndBordersHeight += this.borderWidget('top') + this.borderWidget('bottom');

    this._sizes = {};

    this._childsWidget = [];
};
Widget.prototype = new ObjectHandler();

Widget.prototype.reparent = function(newParentWidget) {
    if (this._parentWidget!=null) this._parentWidget.removeChildWidget(this);
    newParentWidget.addChildWidget(this);
    this._parentWidget = newParentWidget;
};

Widget.prototype.paddingWidget = function(sens) {
    var value = Widget.getStyle(this._nodeWidget, 'padding-'+sens);
    if (value==null) return 0;

    if (value.substr(value.length-2,2)=="px") return Math.floor(parseFloat(value.substr(0, value.length-2)));
    else return Math.floor(value);
};

Widget.prototype.borderWidget = function(sens) {
    var value = Widget.getStyle(this._nodeWidget, 'border-'+sens+'-width');
    if (value==null || value=="medium") return 0;

    if (value.substr(value.length-2,2)=="px") return Math.floor(parseFloat(value.substr(0, value.length-2)));
    else return Math.floor(value);
};

Widget.getStyle = function(node, styleProp) {        

    if (node.currentStyle) {
	if (styleProp=="padding-left") styleProp = 'paddingLeft';
	else if (styleProp=="border-left-width") styleProp = 'borderLeftWidth';
	else if (styleProp=="padding-right") styleProp = 'paddingRight';
	else if (styleProp=="border-right-width") styleProp = 'borderRightWidth';
	else if (styleProp=="padding-top") styleProp = 'paddingTop';
	else if (styleProp=="border-top-width") styleProp = 'borderTopWidth';
	else if (styleProp=="padding-bottom") styleProp = 'paddingBottom';
	else if (styleProp=="border-bottom-width") styleProp = 'borderBottomWidth';

	return node.currentStyle[styleProp];
    }
    
    if (window.getComputedStyle) { // Mozilla
	return window.getComputedStyle(node, null).getPropertyValue(styleProp);
    }

    return "";
};

Widget.prototype.setVerticalLayout = function() { this._layoutWidget = 1; };
Widget.prototype.setHorizontalLayout = function() { 
    this._nodeWidget.style.position = "relative";
    this._nodeWidget.style.top = 0;
    this._nodeWidget.style.left = 0;

    this._layoutWidget = 2; 
};

/**
 * Devrait être appelé avant que le widget soit visible
 */
Widget.prototype.setClassNameWidget = function(className) { 
    this._nodeWidget.className = className; 

    this._paddingsAndBordersWidth = this.paddingWidget('left') + this.paddingWidget('right');
    this._paddingsAndBordersWidth += this.borderWidget('left')+ this.borderWidget('right');

    this._paddingsAndBordersHeight = this.paddingWidget('top') + this.paddingWidget('bottom');
    this._paddingsAndBordersHeight += this.borderWidget('top') + this.borderWidget('bottom');
};
Widget.prototype.setSizes = function(sizes) { 
    var height = null;
    var width = null;
    if (sizes!=null) {
	height = sizes['height'];
	width = sizes['width'];
    }

    if (height!=null) {
	this._sizes['height'] = {};

	if ((typeof height)=="number") this._sizes['height']['fixed'] = height;
	else if (height=='*') {
	    this._sizes['height']['*'] = true;
	    
	} else if (height.substr(height.length-1, 1)=='%') {
	    height = height.substr(0, height.length-1);
	    this._sizes['height']['%'] = parseInt(height, 10);

	} else {
	    Debug.alert("Widget.setSizes() mais height n'est pas conforme");
	    return;
	}
    }

    if (width!=null) {
	this._sizes['width'] = {};

	if ((typeof width)=="number") this._sizes['width']['fixed'] = width;
	else if (width=='*') {
	    this._sizes['width']['*'] = true;
	    
	} else if (width.substr(width.length-1, 1)=='%') {
	    width = width.substr(0, width.length-1);
	    this._sizes['width']['%'] = parseInt(width, 10);

	} else {
	    Debug.alert("Widget.setSizes() mais width n'est pas conforme");
	    return;
	}
    }    
};

Widget._mainWidget = null;
Widget.mainWidget = function() {
    if (Widget._mainWidget==null) Widget._mainWidget = new Widget(null, document.body);

    return Widget._mainWidget;
};

Widget.prototype.parentWidget = function() { return this._parentWidget; };

Widget.prototype.posAbsolute = function() {
    var curleft = curtop = 0;

    if (this._nodeWidget.offsetParent) {
	curleft = this._nodeWidget.offsetLeft;
	curtop = this._nodeWidget.offsetTop;
	var elt = this._nodeWidget.offsetParent;
	while (elt) {
	    curleft += elt.offsetLeft;
	    curtop += elt.offsetTop;
	    elt = elt.offsetParent;
	}
    }

    var elt = this._nodeWidget;
    while (elt && elt!=document.body) {
	curleft -= elt.scrollLeft;
	curtop -= elt.scrollTop;
	elt = elt.parentNode;
    }

    return new Point(curleft, curtop);
};

Widget.prototype.width = function() { 
    if (!this._reallyVisibleWidget) Debug.alert("Widget.width() mais Widget n'est pas visible");
    return this._nodeWidget.offsetWidth; 
};

Widget.prototype.widthInner = function() { 
    if (!this._reallyVisibleWidget) Debug.alert("Widget.widthInner() mais Widget n'est pas visible");
    return this._nodeWidget.offsetWidth - this._paddingsAndBordersWidth;
};

Widget.prototype.setWidth = function(width) {
    this._nodeWidget.style.width = (width-this._paddingsAndBordersWidth)+'px';
};

Widget.prototype.height = function() { 
    if (!this._reallyVisibleWidget) Debug.alert("Widget.height() mais Widget n'est pas visible");
    return this._nodeWidget.offsetHeight; 
};
Widget.prototype.heightInner = function() { 
    if (!this._reallyVisibleWidget) Debug.alert("Widget.heightInner() mais Widget n'est pas visible");
    return this._nodeWidget.offsetHeight - this._paddingsAndBordersHeight;
};

Widget.prototype.setHeight = function(height) {
    this._nodeWidget.style.height = (height-this._paddingsAndBordersHeight)+'px';
};

// Attention
// le node d'une fenêtre (objet Window), est toujours ajouté au document
// contrairement à un simple Widget qui est ajouté au node de son parent
Widget.prototype.addChildWidget = function(widget) {
    this._childsWidget.push(widget);
    if (widget instanceof Window) document.body.appendChild(widget.nodeWidget());
    else {
	// Si a un layout horizontal, alors mettre ses enfants en position absolute
	// sachant que ce widget a été forcé à être en position: relative
	if (this._layoutWidget==2) {
	    widget._nodeWidget.style.position = "absolute";
	    widget._nodeWidget.style.top = 0;
	    widget._nodeWidget.style.left = 0;
	}
	this._nodeWidget.appendChild(widget.nodeWidget());
    }
};

Widget.prototype.removeChildWidget = function(widget) {
    for (var i=0;i<this._childsWidget.length;i++) {
	if (this._childsWidget[i]==widget) {
	    this._childsWidget.splice(i, 1);
	    break;
	}
    }
};

/**
 * Supprime tout le contenu de ce widget
 */
Widget.prototype.clearWidget = function() {
    for (var i=0;i<this._childsWidget.length;i++) this._childsWidget[i].close();
    this._childsWidget = [];

    this._nodeWidget.innerHTML = "";
};

Widget.prototype.nodeWidget = function() { return this._nodeWidget; };

// Pour pouvoir être réécrit dans les classes dérivées
Widget.prototype.close = function() { this.closeWidget(); }; 
Widget.prototype.closeWidget = function() {
    if (this._nodeWidget==null) return; // closeWidget() a déjà été appelé

    this.removeChildsWidget();
    if (this._parentWidget!=null) this._parentWidget.removeChildWidget(this);

    var parentNode = this._nodeWidget.parentNode;
    if (parentNode!=null) {
	parentNode.removeChild(this._nodeWidget);
	this._nodeWidget = null;

	this.trigger("closed");
    }
};

Widget.prototype.removeChildsWidget = function() {
    while (this._childsWidget.length>0) this._childsWidget[0].close();
};

Widget.prototype.isReallyVisibleWidget = function() { return this._reallyVisibleWidget; };
Widget.prototype.isVisibleWidget = function() { return this._visibleWidget; };

/**
 * Ce Widget a été redimensionné
 * Ses dimensions width() et height() sont width et height
 *
 * Redimensionner tous ses fils (visibles)
 * Pour chaque enfant effectivement redimensionné, appeler childWidget->widgetResized(widthChild, heightChild)
 *
 * Tous les Widget n'ont pas la charge des dimensions de leurs enfants
 * C'est à l'utilisateur de le préciser explicitement
 */
Widget.prototype.widgetResized = function() { 
    if (!this._reallyVisibleWidget) return;

    // Redimensionner les enfants visibles
    // Chaque enfant redimensionné redimensionnera tous ses enfants en appelant widgetResized
    this._resizeChilds();
};

// Montrer ce widget
// Comme il faut peut être redimensionner ses frères, c'est le parent de ce widget qui doit exécuter cette action
// Mais si le parent n'est pas visible, alors ce n'est pas la peine
Widget.prototype.show = function() { this.showWidget(); }; // 
Widget.prototype.showWidget = function() { 
    if (this._visibleWidget) return;
    this._visibleWidget = true;

    this._nodeWidget.style.display = this._styleDisplayToBeVisibleWidget;
    //if (this._parentWidget==null || this._parentWidget.isReallyVisibleWidget()) this._nodeWidget.style.display = this._styleDisplayToBeVisibleWidget;

    if (this._parentWidget==null) {
	this._reallyVisibleWidget = true;
	var widgetsBecomeVisible = this._setVisiblesChildsWidget(true);
	this._resizeChilds();
	for (var i=0;i<widgetsBecomeVisible.length;i++) {
	    widgetsBecomeVisible[i].becomeReallyVisible();
	}
	this.becomeReallyVisible();
	return;
    }

    if (!this._parentWidget.isReallyVisibleWidget()) return;

    this._reallyVisibleWidget = true;

    var widgetsBecomeVisible = this._setVisiblesChildsWidget(true);
    this._parentWidget._resizeChilds();
    for (var i=0;i<widgetsBecomeVisible.length;i++) widgetsBecomeVisible[i].becomeReallyVisible();
    this.becomeReallyVisible();
};

Widget.prototype.becomeReallyVisible = function() { };
Widget.prototype.becomeInvisible = function() { };

/**
 * visible : true
 * --------------
 * Ce widget est devenu réellement visible
 * tous ses descendants visibles doivent le devenir
 *
 * visible : false
 * ---------------
 * Ce widget n'est plus visible
 * tous ses descendants visibles doivent devenir invisibles
 */
Widget.prototype._setVisiblesChildsWidget = function(visible) {     
    if (visible) {
	// Renvoyer tous les widgets qui sont devenus visibles pour pouvoir émettre becomeReallyVisible()
	// Cette méthode devant être appelée après que ces widgets aient été redimensionnés
	var widgetsBecomeVisible = [];
	for (var i=0;i<this._childsWidget.length;i++) {
	    if (!this._childsWidget[i].isVisibleWidget()) continue;
	    
	    this._childsWidget[i]._reallyVisibleWidget = true;
	    this._childsWidget[i]._nodeWidget.style.display = this._childsWidget[i]._styleDisplayToBeVisibleWidget;
	    widgetsBecomeVisible.push(this._childsWidget[i]);
	    var childsWidgetsBecomeVisible = this._childsWidget[i]._setVisiblesChildsWidget(true);
	    for (var j=0;j<childsWidgetsBecomeVisible.length;j++) childsWidgetsBecomeVisible[j].becomeReallyVisible();
	}	
	return widgetsBecomeVisible;

    } else {
	for (var i=0;i<this._childsWidget.length;i++) {
	    if (!this._childsWidget[i].isReallyVisibleWidget()) continue;
	    
	    this._childsWidget[i]._reallyVisibleWidget = false;
	    this._childsWidget[i]._nodeWidget.style.display = "none";
	    this._childsWidget[i].becomeInvisible();
	    this._childsWidget[i]._setVisiblesChildsWidget(false);
	}
    }

    return [];
};

Widget.prototype._resizeChilds = function() { 
    this.onWidgetResized();

    if (this._layoutWidget==0) this._resizeChildsWithoutLayout();
    else if (this._layoutWidget==1) this._resizeChildsWithVerticalLayout();
    else if (this._layoutWidget==2) this._resizeChildsWithHorizontalLayout();
};

Widget.prototype.onWidgetResized = function() { 
    
};

Widget.prototype._resizeChildsWithoutLayout = function() { 
    for (var i=0;i<this._childsWidget.length;i++) {
	if (this._childsWidget[i].isVisibleWidget()) this._childsWidget[i]._resizeChilds();
    }
};

// Ne doit être appelé que si this._reallyVisibleWidget = true;
Widget.prototype._resizeChildsWithVerticalLayout = function() { this._resizeChildsWithVerticalLayoutWidget(); };
Widget.prototype._resizeChildsWithVerticalLayoutWidget = function() { 
    if (this._childsWidget.length==0) return;
    
    var heightDispo = this.heightInner(); // la hauteur disponible

    // Ceux dont la hauteur est la hauteur naturelle
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['height']!=null) continue;

	if (w.isVisibleWidget()) heightDispo -= w.height();
    }

    // Ceux dont la largeur est fixée en pixels
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['height']==null || sizes['height']['fixed']==null) continue;

	if (w.isVisibleWidget()) {
	    var height = sizes['height']['fixed'];

	    w.setHeight(height);
	    heightDispo -= height;
	}
    }

    // Ceux dont la largeur est fixée en pourcentage de la largeur restante
    var totalPourcentage = 0;
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['height']==null || sizes['height']['%']==null) continue;

	if (w.isVisibleWidget()) {
	    var height = Math.floor(sizes['height']['%']/100.*heightDispo);
	    w.setHeight(height);
	    totalPourcentage += sizes['height']['%'];
	}
    }

    heightDispo -= Math.floor(heightDispo*totalPourcentage/100.);

    // Celui dont la largeur est la largeur restante
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['height']==null || sizes['height']['*']==null) continue;

	if (w.isVisibleWidget()) w.setHeight(heightDispo);
    }
    
    for (var i=0;i<this._childsWidget.length;i++) {
	if (this._childsWidget[i].isVisibleWidget()) this._childsWidget[i]._resizeChilds();
    }
};

Widget.prototype._resizeChildsWithHorizontalLayout = function() { this._resizeChildsWithHorizontalLayoutWidget(); };
Widget.prototype._resizeChildsWithHorizontalLayoutWidget = function() { 
    if (this._childsWidget.length==0) return;
    
    var widthDispo = this.widthInner(); // la largeur disponible

    // Ceux dont la largeur est la largeur naturelle
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['width']!=null) continue;

	if (w.isVisibleWidget()) widthDispo -= w.width();
    }

    // Ceux dont la largeur est fixée en pixels
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['width']==null || sizes['width']['fixed']==null) continue;

	if (w.isVisibleWidget()) {
	    var width = sizes['width']['fixed'];

	    w.setWidth(width);
	    widthDispo -= width;
	}
    }

    // Ceux dont la largeur est fixée en pourcentage de la largeur restante
    var totalPourcentage = 0;
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['width']==null || sizes['width']['%']==null) continue;

	if (w.isVisibleWidget()) {
	    var width = Math.floor(sizes['width']['%']/100.*widthDispo);
	    w.setWidth(width);
	    totalPourcentage += sizes['width']['%'];
	}
    }

    widthDispo -= Math.floor(widthDispo*totalPourcentage/100.);

    // Celui dont la largeur est la largeur restante
    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	var sizes = w._sizes;
	if (sizes['width']==null || sizes['width']['*']==null) continue;

	if (w.isVisibleWidget()) w.setWidth(widthDispo);
    }

    // Il faut ensuite repositionner les left des éléments en absolu
    var left = this._childsWidget[0].width();
    for (var i=1;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	//w.setLeft(left);
	w._nodeWidget.style.left = left+'px';
	left += w.width();
    }

    // Puis s'occuper des hauteurs, qui est toujours 100% de la hauteur disponible
    var heightDispo = this.heightInner(); // la hauteur disponible

    for (var i=0;i<this._childsWidget.length;i++) {
	var w = this._childsWidget[i];
	if (w instanceof Window) continue;

	if (!w.isVisibleWidget()) continue;

	w.setHeight(heightDispo);
    }

    for (var i=0;i<this._childsWidget.length;i++) {
	if (this._childsWidget[i].isVisibleWidget()) this._childsWidget[i]._resizeChilds();
    }      
};

Widget.prototype.hide = function() { this.hideWidget(); };
Widget.prototype.hideWidget = function() {
    if (!this._visibleWidget) return;
    this._visibleWidget = false;

    /////// A VERIFIER !!!
    // ETAIT APRES this._reallyVisibleWidget = false; MAIS POSE PROBLÈME !!!
    this._nodeWidget.style.display = "none";
    
    if (!this._reallyVisibleWidget) return;
    this._reallyVisibleWidget = false;
    
    // Ce widget était visible
    if (this._parentWidget!=null) this._parentWidget._resizeChilds();

    this.becomeInvisible();
    this._setVisiblesChildsWidget(false);    
};


Widget.prototype.setContentHTML = function(html) {
    this._nodeWidget.innerHTML = html;
};

Widget.prototype.appendContentNode = function(node) {
    this._nodeWidget.appendChild(node);
};

Widget.prototype.prependContentNode = function(node) {
    var firstChild = this._nodeWidget.firstChild;

    if (firstChild==null) this._nodeWidget.appendChild(node);
    else this._nodeWidget.insertBefore(node, firstChild);
};


