
	// empty
	/* Renvoie si un objet est vide */
	function empty(param) {
		var i;
		for (i in param.object) return false;
		return true;
	}
	
	// fusion
	/* Fusionne des objets en un seul. Les objets déclarés en derniers écrasent les premiers s'ils ont la même clé */
	function fusion(param) {
		var objects = param.objects;
		var r = {};
		var i, j, object;
		for (i in objects) {
			var object = objects[i];
			for (j in object) r[j] = object[j];
		}
		return r;
	}
	
	// in_array
	/* Renvoie si un élément est dans le tableau */
	function in_array(param) {
		var element = param.element;
		var array = param.array;
		var i;
		for (i in array) if (array[i] == element) return true;
		return false;
	}
	
	// objCount
	/* Renvoie le nombre d'éléments d'un objet */
	function objCount(param) {
		var object = param.object;
		var i, r = 0;
		for (i in object) r++;
		return r;
	};
	
	// objFirstKey
	/* Renvoie la 1ère clé */
	function objFirstKey(param) {
		var object = param.object;
		var i;
		for (i in object) return i;
		return null;
	}
	
	// setInheirtnace
	/* Crée l'héritage */
	function setInheritance(param) {
		var c = param['class'].prototype;
		var parent = param.parent.prototype;
		var i, m;
		for (i in parent) {
			m = parent[i];
			if (typeof(m) !== 'function') continue;
			if (typeof(c[i]) === 'function') continue;
			c[i] = m;
		}
	};
	
	// setInheritances
	/* Crée l'héritage multiple */
	function setInheritances(param) {
		var c = param['class'];
		var parents = param.parents;
		var i;
		for (i in parents) setInheritance({'class': c, 'parent': parents[i]});
	}

// HasIDs
/* Gère des ids */

	// HasIDs
	/* Constructeur */
	var HasIDs = function() {
		this._HasIDs		=	{};
		
		this._HasIDs.ids	=	{};
	};
	
	HasIDs.uid = 0;
	
	// getID
	/* Renvoie un id */
	HasIDs.prototype.getID = function(param) {
		var id = param.id;
		var ids = this._HasIDs.ids;
		if (ids[id] === undefined) ids[id] = this.getUniqueID();
		return ids[id];
	};
	
	// getUniqueID
	/* Renvoie un id unique */
	HasIDs.prototype.getUniqueID = function() {
		return 'u'+HasIDs.uid++;
	};
	
	// setID
	/* Affecte un id */
	HasIDs.prototype.setID = function(param) {
		this._HasIDs.ids[param.id] = param.value;
	};

// SelectMenuHasElements
/* Element de SelectMenu ayant des éléments */

	// SelectMenuHasElements
	/* Constructeur */
	var SelectMenuHasElements = function() {
		this._SelectMenuHasElements	=	{};
		
		this.setElements({'elements': []});
	};
	
	// getElements
	/* Renvoie les éléments */
	SelectMenuHasElements.prototype.getElements = function() {
		return this._SelectMenuHasElements.elements;
	};
	
	// push
	/* Ajoute un élément à la fin */
	SelectMenuHasElements.prototype.push = function(param) {
		var element = param.element;
		this.getElements().push(element);
		this.onchange();
	};
	
	// setElements
	/* Affecte les éléments */
	SelectMenuHasElements.prototype.setElements = function(param) {
		this._SelectMenuHasElements.elements = param.elements;
	};
	
// SelectMenuElement
/* Element (de) selectMenu */

	var SelectMenuElement = function(param) {
		var param = param || {};
		this._SelectMenuElement	=	{};
		
		var parent = (param.parent !== undefined) ? param.parent : null;
		
		HasIDs.call(this, param);
		
		this.setParent({'parent': parent});
	};
	
	setInheritances({'class': SelectMenuElement, 'parents': [HasIDs]});
	
	// bind
	/* Initialise le widget */
	SelectMenuElement.prototype.bind = function() {
	};
	
	// getParent
	/* Renvoie le parent */
	SelectMenuElement.prototype.getParent = function() {
		return this._SelectMenuElement.parent;
	};
	
	// getSelectMenu
	/* Renvoie le selectmenu */
	SelectMenuElement.prototype.getSelectMenu = function() {
		var element = this;
		var parent;
		while (parent = element.getParent()) element = parent;
		return element;
	};
	
	// html
	/* Renvoie le code html */
	SelectMenuElement.prototype.html = function() {
		return '';
	};
	
	// onchange
	/* Changement */
	SelectMenuElement.prototype.onchange = function() {
	};
	
	// setParent
	/* Affecte le parent */
	SelectMenuElement.prototype.setParent = function(param) {
		this._SelectMenuElement.parent = param.parent;
	};

// SelectMenu
/* Liste de sélection */

	// SelectMenu
	/* Constructeur */
	var SelectMenu = function(param) {
		param = param || {};
		
		this._SelectMenu			=	{};
		this._SelectMenu.options	=	{};
		
		SelectMenuElement.call(this, param);
		SelectMenuHasElements.call(this, param);
		
		var select = param.select;
		if (select) this.setID({'id': 'selectID', 'value': select.attr('id')});
		
		var minWidth = (param.minWidth !== undefined) ? param.minWidth : 75;
		
		var selMax = (param.selMax !== undefined) ? param.selMax : ((select && select.attr('multiple')) ? +Infinity : 1);
		this.setSelMax({'selMax': selMax});
		
		var selMin = (param.selMin !== undefined) ? param.selMin : ((this.isMultiple()) ? 0 : 1);
		
		var selection = {};
		
		this.setMinWidth({'minWidth': minWidth});
		this.setOpened({'opened': false, 'delay': 0});
		this.setSelection({'selection': selection});
		this.setSelMin({'selMin': selMin});
		
		if (select) this.buildFromSelect(param);
		
		var options = this.getOptions();
		var i;
		for (i in options) {
			if (selMin - objCount({'object': selection}) <= 0) break;
			
			var option = options[i];
			if (! option.isSelected()) option.swap();
		}
	};
	
	setInheritances({'class': SelectMenu, 'parents': [SelectMenuElement, SelectMenuHasElements]});
	
	// addOption
	/* Ajoute une option */
	SelectMenu.prototype.addOption = function(param) {
		var option = param.option;
		this._SelectMenu.options[option.getID({'id': 'id'})] = option;
	};
	
	// bind
	/* Initialise le widget */
	SelectMenu.prototype.bind = function(param) {
		var param = param || {};
		
		var select = this;
		var element = $('#'+this.getID({'id': 'element'}));
		var bind = (param.bind !== undefined) ? param.bind : true;
		
		var m = (bind) ? 'bind' : 'unbind';
		
		element[m]('mouseenter.selectmenu', function() { select.onmouseenter(); });
		element[m]('mouseleave.selectmenu', function() { select.onmouseleave(); });
		
		var elements = this.getElements();
		var i;
		for (i in elements) elements[i].bind({'bind': bind});
		
		this.onselectionchange();
		this.onchange();
	};
	
	// buildFromSelect
	/* Construit la liste depuis un select */
	SelectMenu.prototype.buildFromSelect = function(param) {
		var select = param.select;
		select.hide();
		
		if (param.onchange) select.change(param.onchange);
		
		var selectMenu = this;
		var classOption = this.getClassOption();
		var optIndex = 0;
		
		var selDefault = ($('#'+select.attr('id')+' option[selected=selected]').length === 0);
		
		var brow = function(param) {
			var parent = param.parent;
			var element = param.element;
			var childs = element.children();
			var len = childs.size();
			
			if (len === 0) {	// option
				var option = new classOption({'parent': parent, 'value': element.attr('value'), 'content': element.html(), 'selected': element.attr('selected') && ! selDefault, 'index': optIndex++});
				parent.push({'element': option});
			}
			else {	// optgroup
				var optGroup = new SelectMenuOptGroup({'parent': parent});
				parent.push({'element': optGroup});
				childs.each(function() { brow({'parent': optGroup, 'element': $(this)}); });
			}
		};
		select.children().each(function() { brow({'parent': selectMenu, 'element': $(this)}); });
		
		var t = this.html();
		$(t).insertAfter(select);
		this.bind();
	};
	
	// getCss
	/* Renvoie la class css */
	SelectMenu.prototype.getCss = function() {
		return 'ria-selectmenu'+((this.isMultiple()) ? ' multiple' : '');
	};
	
	// getClassOption
	/* Renvoie la class de option */
	SelectMenu.prototype.getClassOption = function() {
		return (this.isMultiple()) ? SelectMenuOptionMultiple : SelectMenuOption;
	};
	
	// getMinWidth
	/* Renvoie la largeur minimum */
	SelectMenu.prototype.getMinWidth = function() {
		return this._SelectMenu.minWidth;
	};
	
	// getOptions
	/* Renvoie les options */
	SelectMenu.prototype.getOptions = function() {
		return this._SelectMenu.options;
	};
	
	// getOptionsByIndex
	/* Renvoie les options par index */
	SelectMenu.prototype.getOptionsByIndex = function() {
		var r = [];
		
		var i, j, k;
		var options = this.getOptions();
		for (i in options) {
			var index = options[i].getIndex();
			for (j=0; j<r.length; j++) if (index <= r[j].getIndex()) break;
			for (k=r.length; k>j; k--) r[k] = r[k-1];
			r[j] = options[i];
		}
		
		return r;
	};
	
	// getSelection
	/* Renvoie la sélection */
	SelectMenu.prototype.getSelection = function() {
		return this._SelectMenu.selection;
	};
	
	// getSelMax
	/* Renvoie sel max */
	SelectMenu.prototype.getSelMax = function() {
		return this._SelectMenu.selMax;
	};
	
	// getSelMin
	/* Renvoie sel min */
	SelectMenu.prototype.getSelMin = function() {
		return this._SelectMenu.selMin;
	};
	
	// getStatus
	/* Renvoie le nouveau status */
	SelectMenu.prototype.getStatus = function() {
		var selection = this.getSelection();
		var n = objCount({'object': selection});
		var count = objCount({'object': this.getOptions()});
		
		var s;
		if (n === 0) s = '';
		else if (n === 1) s = selection[objFirstKey({'object': selection})].getContent();
		else s = n+' option'+((n > 1) ? 's' : '')+' sélectionné'+((n > 1) ? 's' : '');
		
		return s;
	}
	
	// html
	/* Renvoie le code html */
	SelectMenu.prototype.html = function() {
		var t = '';
		t += '<div id="'+this.getID({'id': 'element'})+'" class="'+this.getCss()+'">';
			t += '<div id="'+this.getID({'id': 'status'})+'" class="ria-selectmenu-status">';
				t += '<div id="'+this.getID({'id': 'status-inner'})+'" class="ria-selectmenu-status-inner">Select status</div>';
			t += '</div>';
			t += '<div id="'+this.getID({'id': 'button'})+'" class="ria-selectmenu-button"></div>';
			t += '<div class="ria-selectmenu-clr"></div>';
			
			t += '<div id="'+this.getID({'id': 'menu-container'})+'" class="ria-selectmenu-menu-container">';
				t += '<div id="'+this.getID({'id': 'menu'})+'" class="ria-selectmenu-menu">';
					var elements = this.getElements();
					var i;
					for (i in elements) t += elements[i].html();
				t += '</div>';
			t += '</div>';
		t += '</div>';
		t += '<div class="ria-selectmenu-clr"></div>';
		
		return t;
	};
	
	// isMultiple
	/* Renvoie si la sélection est multiple */
	SelectMenu.prototype.isMultiple = function() {
		return (this.getSelMax() > 1);
	};
	
	// isOpened
	/* Renvoie si la liste est ouverte */
	SelectMenu.prototype.isOpened = function() {
		return this._SelectMenu.opened;
	};
	
	// onchange
	/* Changement */
	SelectMenu.prototype.onchange = function() {
		$('#'+this.getID({'id': 'status'})).css('min-width', this.getMinWidth());
		this.onstatuschange();
		
		var selectMenu = this;
		setTimeout(function() {
			$('#'+selectMenu.getID({'id': 'menu-container'})).css('width', 'auto').css('height', 'auto').css('overflow', 'visible');
			selectMenu.setOpened({'opened': selectMenu.isOpened(), 'delay': 0}, 1);
		});
	};
	
	// onmouseenter
	/* Event mouseenter */
	SelectMenu.prototype.onmouseenter = function() {
		this.setOpened({'opened': true});
	};
	
	// onmouseleave
	/* Event mouseleave */
	SelectMenu.prototype.onmouseleave = function() {
		this.setOpened({'opened': false});
	};
	
	// onselectionchange
	/* Changement selection */
	SelectMenu.prototype.onselectionchange = function(param) {
		param = param || {};
		var trigger = (param.trigger !== undefined) ? param.trigger : false;
		
		this.setStatus({'status': this.getStatus()});
		if (trigger) this.trigger();
	};
	
	// onstatuschange
	/* Changement status */
	SelectMenu.prototype.onstatuschange = function() {
		var status = $('#'+this.getID({'id': 'status'}));
		var menu = $('#'+this.getID({'id': 'menu'}));
		
		var wStatus = status.width();
		
		menu.width('auto');
		var wLayer = menu.width();
		
		if (wLayer < wStatus) menu.width(wStatus);
	};
	
	// selOption
	/* Sélectionne/Déselectionne une option */
	SelectMenu.prototype.selOption = function(param) {
		var option = param.option;
		var sel = (param.sel !== undefined) ? param.sel : true;
		var selection = this.getSelection();
		var count = objCount({'object': selection});
		var onselectionchange = (param.onselectionchange !== undefined) ? param.onselectionchange : true;
		var selMin = this.getSelMin();
		var selMax = this.getSelMax();
		var verify = (param.verify !== undefined) ? param.verify : true;
		
		var newCount = count + ((sel) ? 1 : -1);
		
		if ((count < selMin || newCount >= selMin) && (count > selMax || newCount <= selMax) || ! verify) {
			var id = option.getID({'id': 'id'});
			if (sel) selection[id] = option;
			else {
				selection[id] = null;
				delete selection[id];
			}
			option.onchange();
			$('#'+this.getID({'id': 'selectID'})+' option[value="'+option.getValue()+'"]')[sel ? 'attr' : 'removeAttr']('selected', 'selected');
			if (onselectionchange) this.onselectionchange({'trigger': param.trigger});
			return true;
		}
		else if (count === 1 && newCount === 2) {
			// special 1
			var key = objFirstKey({'object': selection});
			this.selOption({'option': selection[key], 'sel': false, 'verify': false, 'onselectionchange': false});
			this.selOption(param);
			return true;
		}
		else return false;
	};
	
	// setMinWidth
	/* Affecte minWidth */
	SelectMenu.prototype.setMinWidth = function(param) {
		this._SelectMenu.minWidth = param.minWidth;
		this.onchange();
	};
	
	// setOpened
	/* Ouvre/Ferme le menu */
	SelectMenu.prototype.setOpened = function(param) {
		var opened = param.opened;
		this._SelectMenu.opened = opened;
		
		var delay = (param.delay !== undefined) ? param.delay : undefined;
		
		$('#'+this.getID({'id': 'menu-container'}))[opened ? 'fadeIn' : 'fadeOut'](delay);
	};
	
	// setSelection
	/* Affecte la selection */
	SelectMenu.prototype.setSelection = function(param) {
		this._SelectMenu.selection = param.selection;
		this.onselectionchange();
	};
	
	// setSelMax
	/* Affecte sel max */
	SelectMenu.prototype.setSelMax = function(param) {
		this._SelectMenu.selMax = param.selMax;
	};
	
	// setSelMin
	/* Renvoie sel min */
	SelectMenu.prototype.setSelMin = function(param) {
		this._SelectMenu.selMin = param.selMin;
	};
	
	// setStatus
	/* Affecte le status */
	SelectMenu.prototype.setStatus = function(param) {
		$('#'+this.getID({'id': 'status-inner'})).html(param.status);
		this.onstatuschange();
	};
	
	// sortSelectionByIndex
	/* Tri la sélection par index */
	SelectMenu.prototype.sortSelectionByIndex = function() {
		var t = [];
		var i, j, k;
		var selection = this.getSelection();
		for (i in selection) {
			var index = selection[i].getIndex();
			for (j=0; j<t.length; j++) if (index <= t[j].getIndex()) break;
			for (k=t.length; k>j; k--) t[k] = t[k-1];
			t[j] = selection[i];
			
			selection[i] = null;
			delete selection[i];
		}
		for (i in t) selection[t[i].getID({'id': 'id'})] = t[i];
	};
	
	// swap
	/* Permute une option */
	SelectMenu.prototype.swap = function(param) {
		var option = param.option;
		return this.selOption({'option': option, 'sel': ! option.isSelected(), 'trigger': param.trigger});
	};
	
	// trigger
	/* Déclengeur onchange */
	SelectMenu.prototype.trigger = function() {
		$('#'+this.getID({'id': 'selectID'})).change();
	};

// SelectMenuOptGroup
/* Groupe d'options */

	// SelectMenuOptGroup
	/* Constructeur */
	var SelectMenuOptGroup = function(param) {
		this._SelectMenuOptGroup	=	{};
		
		SelectMenuElement.call(this, param);
		SelectMenuHasElements.call(this, param);
	};
	
	setInheritances({'class': SelectMenuOptGroup, 'parents': [SelectMenuElement, SelectMenuHasElements]});
	
	// bind
	/* Initialise l'optGroup */
	SelectMenuOptGroup.prototype.bind = function(param) {
		var param = param || {};
		
		var bind = (param.bind !== undefined) ? param.bind : true;
		var m = (bind) ? 'bind' : 'unbind';
		
		var elements = this.getElements();
		var i;
		for (i in elements) elements[i].bind({'bind': bind});
		this.onchange();
	};
	
	// html
	/* Renvoie le code html */
	SelectMenuOptGroup.prototype.html = function() {
		var t = '';
		t += '<div class="ria-selectmenu-optgroup-label">';
			t += 'OPTGROUP';
		t += '</div>';
		t += '<div class="ria-selectmenu-optgroup-options">';
			var elements = this.getElements();
			var i;
			for (i in elements) t += elements[i].html();
		t += '</div>';
		return t;
	};

// SelectMenuOption
/* Option */

	// SelectMenuOption
	/* Constructeur */
	var SelectMenuOption = function(param) {
		param = param || {};
		this._SelectMenuOption	=	{};
		
		SelectMenuElement.call(this, param);
		
		var addOption = (param.addOption !== undefined) ? param.addOption : true;
		var swap = (param.swap !== undefined) ? param.swap : true;
		
		this.setContent({'content': param.content});
		this.setIndex({'index': param.index});
		this.setValue({'value': param.value});
		
		if (addOption) this.getSelectMenu().addOption({'option': this});
		if (swap && param.selected) this.swap();
	};
	
	setInheritances({'class': SelectMenuOption, 'parents': [SelectMenuElement]});
	
	// bind
	/* Initialise l'option */
	SelectMenuOption.prototype.bind = function(param) {
		var param = param || {};
		
		var option = this;
		var bind = (param.bind !== undefined) ? param.bind : true;
		var m = (bind) ? 'bind' : 'unbind';
		
		var element = $('#'+this.getID({'id': 'element'}));
		element[m]('click.selectmenu', function() { option.swap({'trigger': true}); });
		this.onchange();
	};
	
	// getContent
	/* Renvoie le contenu */
	SelectMenuOption.prototype.getContent = function() {
		return this._SelectMenuOption.content;
	};
	
	// getIndex
	/* Renvoie l'index */
	SelectMenuOption.prototype.getIndex = function() {
		return this._SelectMenuOption.index;
	};
	
	// getValue
	/* Renvoie la valeur */
	SelectMenuOption.prototype.getValue = function() {
		return this._SelectMenuOption.value;
	};
	
	// html
	/* Renvoie le code html */
	SelectMenuOption.prototype.html = function() {
		var t = '';
		t += '<div id="'+this.getID({'id': 'element'})+'" class="ria-selectmenu-option">';
			t += '<span class="ria-selectmenu-option-content">'+this.getContent()+'</span>';
		t += '</div>';
		return t;
	};
	
	// isSelected
	/* Renvoie si l'option est sélectionnée */
	SelectMenuOption.prototype.isSelected = function() {
		return (this.getSelectMenu().getSelection()[this.getID({'id': 'id'})] ? true : false);
	};
	
	// onchange
	/* Changement */
	SelectMenuOption.prototype.onchange = function() {
		$('#'+this.getID({'id': 'element'}))[this.isSelected() ? 'addClass' : 'removeClass']('selected');
	};
	
	// setContent
	/* Affecte le contenu */
	SelectMenuOption.prototype.setContent = function(param) {
		this._SelectMenuOption.content = param.content;
	};
	
	// setIndex
	/* Affecte l'index */
	SelectMenuOption.prototype.setIndex = function(param) {
		this._SelectMenuOption.index = param.index;
	};
	
	// setValue
	/* Affecte la valeur */
	SelectMenuOption.prototype.setValue = function(param) {
		this._SelectMenuOption.value = param.value;
	};
	
	// swap
	/* Permutte la sélection */
	SelectMenuOption.prototype.swap = function(param) {
		param = param || {};
		return this.getSelectMenu().swap({'option': this, 'trigger': param.trigger});
	};

// SelectMenuOptionMultiple
/* Option Multiple */

	// SelectMenuOptionMultiple
	/* Constructeur */
	var SelectMenuOptionMultiple = function(param) {
		param = param || {};
		this._SelectMenuOptionMultiple	=	{};
		
		SelectMenuOption.call(this, fusion({'objects': [{'addOption': false, 'swap': false}, param]}));
		
		this.setContent({'content': param.content});
		this.setValue({'value': param.value});
		
		this.getSelectMenu().addOption({'option': this});
		
		if (param.selected) this.swap();
	};
	
	setInheritances({'class': SelectMenuOptionMultiple, 'parents': [SelectMenuOption]});
	
	// bind
	/* Initialise l'option */
	SelectMenuOptionMultiple.prototype.bind = function(param) {
		var option = this;
		$('#'+this.getID({'id': 'checkbox'})).bind('click.selectmenu', function(e) {
			e.stopPropagation();
			return option.swap({'trigger': true});
		});
		
		SelectMenuOption.prototype.bind.call(this, param);
	};
	
	// html
	/* Renvoie le code html */
	SelectMenuOptionMultiple.prototype.html = function() {
		var t = '';
		t += '<div id="'+this.getID({'id': 'element'})+'" class="ria-selectmenu-option">';
			t += '<input id="'+this.getID({'id': 'checkbox'})+'" name="'+this.getSelectMenu().getID({'id': 'selectID'})+'[]='+this.getValue()+'" type="checkbox" value="'+this.getValue()+'" />';
			t += '<span class="ria-selectmenu-option-content">'+this.getContent()+'</span>';
		t += '</div>';
		return t;
	};
	
	// onchange
	/* Changement */
	SelectMenuOptionMultiple.prototype.onchange = function() {
		var selected = this.isSelected();
		$('#'+this.getID({'id': 'checkbox'}))[selected ? 'attr' : 'removeAttr']('checked', 'checked');
		$('#'+this.getID({'id': 'element'}))[selected ? 'addClass' : 'removeClass']('selected');
	};

