if(GA === undefined) {
	var GA = {};
}

(function(namespace) {
	namespace = namespace || window;
	var $ = jQuery;
	
	// Class names defaults
	var classNames = {
		common: {
			hide: 'hide'
		},
		select: {
			container: 'fauxSelect',
			focus: 'focus',
			open: 'opened',
			close: 'closed',
			select: 'selected',
			hover: 'hover',
			scroll: 'scrolling'
		},
		checkbox: {
			container: 'fauxCheckbox',
			check: 'checked',
			focus: 'focus'
		},
		radio: {
			container: 'fauxRadio',
			check: 'checked',
			focus: 'focus'
		}
	};
	
	// Change class names defaults
	var setClassNames = function(type, datas) {
		var o;
		switch(type.toLowerCase()) {
			case 'select':
				o = classNames.select;
				break;
			case 'checkbox':
				o = classNames.checkbox;
				break;
			case 'radio':
				o = classNames.radio;
				break;
			case 'common':
				o = classNames.common;
				break;
			default:
				throw new Error('FauxFields.setClassNames does not support this field type');
				return;
		}
		for(var k in datas) {
			if(o[k]) {o[k] = datas[k];}
		}
	};
	
	// Faux Select Constructor
	var Select = function(datas) {
		if(!(this instanceof Select)) {
			return new Select(datas);
		}
		
		Select.superclass.constructor.call(this);
		
		this.addEvents(['change'], datas.onload);
		this.initialize(datas);
	};
	namespace.extend(Select, namespace.Observable, {
		// private
		initialize: function(datas) {
			var that = this;
			
			if(typeof datas.field == 'string') {
				var s = datas.field.charAt(0) == '#' ? datas.field : '#' + datas.field;
			} else {
				s = datas.field;
			}
			
			this.field =         $(s).focus(function() {that.focus();})
			                         .blur(function() {that.blur();})
			                         .keyup(function(e) {that.keySelect(e);})
			                         .addClass(classNames.common.hide)
			                         .get(0);
			var revCls = $(this.field).hasClass('rev') ? ' class="rev"' : '';
      this.lastValue =     null;
			this.lastIndex =     null;
			this.lastOption =    null;
			this.maxHeight =     datas.maxHeight || null;
			this.preventClose =  false;
			this.timer =         null;
			this.values =        {};
			this.fauxFrame =     $('<div id="' + this.field.id + 'REP" class="'+ classNames.select.container + ' ' + classNames.select.close + '"><span></span><ul' + revCls + '></ul></div>');
			this.fauxCurrent =   $(this.fauxFrame[0].firstChild).click(function() {that.clickFrame();});
			this.fauxList =      $(this.fauxFrame[0].lastChild);
			this.toDisable =     datas.disable ? $(datas.disable) : null;
			
			this.fill();
			this.fauxFrame.insertAfter(this.field);
			this.height(this.maxHeight);
			this.fireEvent('load');
		},
		
		fill: function(datas, clean) {
			if(datas) {
				if(clean) {this.field.options.length = 0;}
				var k = -1, sel;
				while(datas[++k]) {
					if(datas[k][2]) {sel = k;}
					this.field.options[this.field.options.length] = new Option(datas[k][0], datas[k][1]);
				}
				this.field.selectedIndex = sel;
			}
			var that = this, li = '', s, sO, sI = this.field.selectedIndex, v, c = 0;
			this.fauxCurrent.text(this.field.options[sI].innerHTML);
			$(this.field).children().each(function(i) {
				sO = (this.className) ? ' ' + this.className : '';
				s = ' class="i' + i + sO + '"';
				li += '<li' + s + '>' + this.innerHTML + '</li>';
				v = this.value || 'empty' + (++c);
				that.values[v] = i;
			});
			this.fauxList.click(function(e) {
				if(e.target == that.fauxList[0]) {
					that.preventClose = true;
					return true;
				}
				that.update(e.target);
				that.timer = setTimeout(function() {that.field.focus();}, 20);
			})[0].innerHTML = li;
			this.items = this.fauxList.children();
			// hover for IE 6 (pseudo-class :hover only supported on links)
			if(window.ie6) {
				this.items.hover(that.mouseover, that.mouseout);
			}
			if(datas) {
				this.height(this.maxHeight);
			}
			this.lastOption = $(this.items[sI]);
			this.selectLi(this.items[sI]);
		},
		
		height: function(h) {
			if(!h) {
				return this.fauxList.height();
			}
			if(this.fauxList.height() > h) {
				this.fauxList.addClass(classNames.select.scroll).height(h);
				return;
			} else {
				this.fauxList.height('auto');
			}
		},
		
		deselectLi: function() {
			this.lastOption.removeClass(classNames.select.select);
		},
		
		selectLi: function(li) {
			this.fauxCurrent.text(li.innerHTML.replace('amp;',''));
			this.lastOption = $(li).addClass(classNames.select.select);
			if(this.maxHeight) {
				var sTop = this.fauxList[0].scrollTop;
				var sMax = sTop + this.maxHeight;
				var oTop = sTop + this.lastOption.offset().top - this.fauxList.offset().top;
				var oMax = oTop + this.lastOption[0].offsetHeight;
				if(oTop < sTop || oMax > sMax) {this.fauxList[0].scrollTop = oTop;}
			}
			if(this.lastIndex != this.field.selectedIndex) {
				this.lastIndex = this.field.selectedIndex;
				this.lastValue = this.field.value;
				this.fireEvent('change');
				$(this.field).change();
			}
		},
		
		update: function(li, preventClose) {
			var newLi;
			if(typeof li == 'string') {
				if(this.values[li]) {
					this.field.value = li;
					newLi = this.items[this.values[li]];
				}
			} else if(li.nodeName && li.nodeName.toLowerCase() == 'li') {
				this.field.selectedIndex = li.className.match(/\d+/);
				newLi = li;
			}
			this.deselectLi();
			this.selectLi(newLi);
			if(!preventClose) {this.close();}
		},
		
		disable: function() {
			this.field.disabled = true;
			if(this.toDisable) {
				this.toDisable.addClass('disabled');
			}
		},
		
		enable: function() {
			this.field.disabled = false;
			if(this.toDisable) {
				this.toDisable.removeClass('disabled');
			}
		},
		
		open: function() {
			var that = this;
			$(document).bind('mousedown.fauxSelect', function(e) {
				var t = e.target;
				while(t.parentNode && t.nodeName.toLowerCase() != 'div') {
					if($(t).attr('for') == that.field.id) {return;}
					t = t.parentNode;
				}
				if(!t || (t && t != that.fauxFrame[0])) {that.close();}
			});
			// TODO : manage opening direction (up/down)
			this.fauxFrame.removeClass(classNames.select.close).addClass(classNames.select.open);
			this.field.focus();
		},
		
		close: function(keepFocus) {
			$(document).unbind('mousedown.fauxSelect');
			this.fauxFrame.removeClass(classNames.select.open).addClass(classNames.select.close);
			if(keepFocus) {
				this.field.focus();
			}
		},
		
		focus: function() {
			if(this.field.disabled) {return;}
			this.fauxFrame.addClass(classNames.select.focus);
			clearTimeout(this.timer);
		},
		
		blur: function() {
			if(!this.preventClose) {
				this.fauxFrame.removeClass(classNames.select.focus);
			}
			this.preventClose = false;
		},
		
		clickFrame: function() {
			if(this.field.disabled) {return;}
			this.fauxFrame.hasClass(classNames.select.open) ? this.close(true) : this.open();
		},
		
		keySelect: function(e) {
			
			var that = this, k = e.which;
			if(k == 9) {
				that.close();
			}
			if(e.altKey && (k == 38 || k == 40)) {
				that.clickFrame();
				return;
			}
			if(that.fauxFrame.hasClass(classNames.select.open)) {
				switch(k) {
					case 13:
					case 27:
						that.close();
						break;
					case 37:
					case 38:
						if(that.lastIndex > 0) {that.update(that.items[that.lastIndex - 1], true);}
						break;
					case 39:
					case 40:
						if(that.lastIndex + 1 < that.items.length) {that.update(that.items[that.lastIndex + 1], true);}
						break;
					case 34:
					case 35:
						that.update(that.items[that.items.length - 1], true);
						break;
					case 33:
					case 36:
						that.update(that.items[0], true);
						break;
					default:
						that.deselectLi();
						that.selectLi(that.items[that.field.selectedIndex]);
				}
			} else {
				that.deselectLi();
				that.selectLi(that.items[that.field.selectedIndex]);
			}
		},
		
		mouseover: function() {
			$(this).addClass(classNames.select.hover);
		},
		
		mouseout: function() {
			$(this).removeClass(classNames.select.hover);
		}
	});
	
	// Faux Checkbox Constructor
	var Checkbox = function(datas) {
		if(!(this instanceof Checkbox)) {
			return new Checkbox(datas);
		}
		
		Checkbox.superclass.constructor.call(this);
		
		this.addEvents(['change'], datas.onload);
		this.initialize(datas);
	};
	namespace.extend(Checkbox, namespace.Observable, {
		initialize: function(datas) {
			var that = this;
			
			if(typeof datas.field == 'string') {
				var s = datas.field.charAt(0) == '#' ? datas.field : '#' + datas.field;
			} else {
				s = datas.field;
			}
			
			this.field = $(s).focus(function() {that.focus();})
			                 .blur(function() {that.blur();})
			                 .click(function() {that.clickFrame();})
			                 .addClass(classNames.common.hide)
			                 .get(0);
			var c = (this.field.checked) ? ' class="' + classNames.checkbox.check + '"' : '';
			this.fauxFrame = $('<div id="' + this.field.id + 'REP" class="'+ classNames.checkbox.container + '"><div' + c + '></div></div>').click(function() {that.clickFrame();}).insertAfter(this.field);
			this.fauxBox = $(this.fauxFrame[0].firstChild);
		},
		
		check: function() {
			this.field.checked = true;
			this.fauxBox.addClass(classNames.checkbox.check);
			this.fireEvent('change');
		},
		
		uncheck: function() {
			this.field.checked = false;
			this.fauxBox.removeClass(classNames.checkbox.check);
			this.fireEvent('change');
		},
		
		clickFrame: function() {
			this.fauxBox.hasClass(classNames.checkbox.check) ? this.uncheck() : this.check();
		},
		
		focus: function() {
			this.fauxFrame.addClass(classNames.checkbox.focus);
		},
		
		blur: function() {
			this.fauxFrame.removeClass(classNames.checkbox.focus);
		}
	});
	
	// Stock the last checked radio ID for each radio group
	var radioLast = {};
	
	// Faux Checkbox Radio Constructor
	var Radio = function(datas) {
		if(!(this instanceof Radio)) {
			return new Radio(datas);
		}
		
		Radio.superclass.constructor.call(this);
		
		this.addEvents(['change'], datas.onload);
		this.initialize(datas);
	};
	namespace.extend(Radio, namespace.Observable, {
		initialize: function(datas) {
			var that = this;
			
			if(typeof datas.field == 'string') {
				var s = datas.field.charAt(0) == '#' ? datas.field : '#' + datas.field;
			} else {
				s = datas.field;
			}
			
			this.field = $(s).focus(function() {that.focus();})
			                 .blur(function() {that.blur();})
			                 .click(function() {that.check();})
			                 .addClass(classNames.common.hide)
			                 .get(0);
			this.name = this.field.name;
			this.fauxID = '#' + this.field.id + 'REP';
			var c = '';
			if(this.field.checked) {
				c = ' class="' + classNames.radio.check + '"';
				radioLast[this.name] = this.fauxID;
			}
			this.fauxFrame = $('<div id="' + this.field.id + 'REP" class="'+ classNames.radio.container + '"><div' + c + '></div></div>').click(function(e) {that.check(e);}).insertAfter(this.field);
			this.fauxBox = $(this.fauxFrame[0].firstChild);
		},
		
		check: function(e) {
			var verif = this.fauxBox.hasClass(classNames.radio.check);
			this.field.checked = true;
			if(radioLast[this.name]) {
				$(radioLast[this.name]).children().removeClass(classNames.radio.check);
			}
			this.fauxBox.addClass(classNames.radio.check);
			radioLast[this.name] = this.fauxID;
			if(!verif) {
				if(e) {this.field.click();}
				this.fireEvent('change');
			}
		},
		
		focus: function() {
			this.fauxFrame.addClass(classNames.radio.focus);
		},
		
		blur: function() {
			this.fauxFrame.removeClass(classNames.radio.focus);
		}
	});
	
	// Faux Fields Composite
	var Factory = function(datas) {
		if(!(this instanceof Factory)) {
			return new Factory(datas);
		}
		
		this.initialize(datas);
	};
	namespace.extend(Factory, {
		initialize: function(datas) {
			var that = this,
				selectors = {
					select: 'select',
					checkbox: 'input[type=checkbox]',
					radio: 'input[type=radio]'
				},
				field = datas.field.toLowerCase();
			
			if(!selectors[field]) {
				throw new Error('FauxFields.Composite does not support this field type');
			}
			
			var fn = {
				select: Select,
				checkbox: Checkbox,
				radio: Radio
			};
			
			this.collection = {};
			
			var s = '';
			if(datas.context) {
				s = '#' + datas.context + ' ' + selectors[field];
			}
			$(s).each(function() {
				datas.field = this;
				that.collection[this.id] = new fn[field](datas);
			});
		}
	});
	
	if(!namespace.FauxFields) {
		namespace.FauxFields = {
			Factory: Factory,
			Select: Select,
			Checkbox: Checkbox,
			Radio: Radio,
			setClassNames: setClassNames
		};
	}
})(GA);