$(document).ready(function() {
	//affichage du spinner
	$("#spinner").ajaxStart(function(){ $(this).show(); }).ajaxStop(function(){ $(this).hide(); });

	//focus sur le 1er controle d'une forme (géré en php)
	$("input.focus").focus();

	//affiche le libellé ALT dans le texte si la zone est vide (utile si pas de caption)
	$("input[alt],textearea[alt]").each(function(){
		if(!$(this).val()) $(this).addClass('inputsvide').val($(this).attr('alt'));
		$(this).focus(function() {
	        $(this).val('').removeClass('inputsvide');
	    }).blur(function() {
	        if(!$(this).val()) $(this).addClass('inputsvide').val($(this).attr('alt'));
	        if($(this).val()==$(this).attr('alt')) $(this).addClass('inputsvide');
	    });
  	//$("form:prev").onsubmit()...
	});
});

/** Créer un plugin pour JQ
jQuery.fn.monPlugin = function() {
    this.val('toto');
    return this;
};
**/
function show_media(id)
	{
	$('#media_data').load('medias/show/'+id,function(){
			$('#media_liste').addClass('small');
			$('#media_fiche').show();
			});
	//$('#media_data').scrollTo('#media_'+id);
	return false;
	}
function close_media()
	{
	$('#media_liste').removeClass('small');
	$('#media_fiche').hide();
	return false;
	}
function tab_media(me)
	{
	$('.tm').removeClass('selected');
	$(me).addClass('selected');
	$('.tmediac').removeClass('selected');	
	$('#'+$(me).attr('id')+'c').addClass('selected')
	return false;
	}

function set_cookie(name, value)
		{
		var d = new Date();
		d.setTime(d.getTime() + (86400000 * parseFloat(365)));
		var expire = '; expires=' + d.toGMTString();		
		return (document.cookie = escape(name) + '=' + escape(value || '') + expire);
		}
		
function del_cookie(cookiename)
		{
		set_cookie(name,'');
		}
function get_cookie(name)
		{
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++)
			{
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
			}
		return null;	
		}

/*** LA BOX, TAB, MENU, TIP  et Autocomplete (récup.) ***/
var flyBox = {
    obj : null,
    root : null,
    lastMouseX : 0,
    lastMouseY : 0,
	w:0,
	what:null,
	href :"",
	me:null,
	buffer:'',
	dark:true,
    show: function(me,what)
		{
		//params
		flyBox.what=(what?what:"#flywin");
		var t=$(me).attr("title");
		var w=$(me).attr("width") || 500;flyBox.w=w;
		var h=$(me).attr("height");
		var r=$(me).attr("href");
		flyBox.close();

		//title
		if(t) {$(flyBox.what+"t").html(t).show();$("#flyclose").show();}
		else  {$(flyBox.what+"t").hide();$("#flyclose").hide();}

		//size
		$(flyBox.what+'c').css('height',h ? h+"px" : 'auto');
		if(flyBox.dark) $("#dark").show();
		$(flyBox.what).css('width',w?w+"px":'auto');

		//content & show
		flyBox.href=r;
		flyBox.buffer='';
		if(jQuery.browser.msie)
			{
			flyBox.me=me;
			if(!r) r="#";
			$(me).attr("href","javascript:void(0);");
			}
		if(!r || r.substr(0,1)=="#")
			{
			if(r)
				{
				flyBox.buffer=$(r).html();
				$(r).empty();
				$(flyBox.what+"c").html(flyBox.buffer);
				}
			flyBox.position();
			$(flyBox.what).show();
			try { $(".focus").focus(); } catch(e) {};
			}
		else
		    {
			//$("#spinner").show();
            $(flyBox.what+"c").load(r,function(){
				flyBox.position();
				$(flyBox.what).show();
				//$("#spinner").hide();
                try { $(".focus").focus(); } catch(e) {};
				});
			}
		if(flyBox.w) this.dragInit(flyBox.what.substring(1)+"t",flyBox.what.substring(1));

        if(flyBox.dark) $("#dark").click(function() { flyBox.close(); } );

		return false;
		},
	close:function()
		{
		$(flyBox.what).fadeOut("fast",function(){ if(flyBox.dark) $("#dark").hide();});
		//$(flyBox.what).hide();
		//if(flyBox.dark) $("#dark").hide();
		if(jQuery.browser.msie) $(flyBox.me).attr("href",flyBox.href);
		if(flyBox.buffer) $(flyBox.href).html(flyBox.buffer);
		return false;
		},
	position:function()
		{
	    var de = document.documentElement;
	    var y = self.pageYOffset || (de&&de.scrollTop) || document.body.scrollTop;
	    var h = window.innerHeight || self.innerHeight || (de&&de.clientHeight) || document.body.clientHeight;

	    //if(flyBox.dark) $("#dark").css("height",h+y+"px");
	    if(flyBox.w)
	    	{
	    	var w=window.innerWidth || self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
	    	w=(w/2)-(flyBox.w/2);
			$(flyBox.what).css({top:100+y,left:w});
	    	}
	    else $(flyBox.what).css({top:100+y,left:"50%",marginLeft:"-"+(w)+"px"});
		},
	dragInit:function(id, idRoot)
	    {
	    flyBox.obj=document.getElementById(id);
	    flyBox.root=document.getElementById(idRoot);
	    $(flyBox.obj).css('cursor','move').bind('mousedown',function(e){flyBox.dragStart(e);});
	    },
	dragStart:function(e)
		{
	    $(flyBox.root).css('opacity',0.7);
	    flyBox.lastMouseX = e.clientX;
	    flyBox.lastMouseY = e.clientY;
	    $(document).bind('mousemove', function(e){flyBox.drag(e);});
	    $(document).bind('mouseup', function(e){flyBox.dragEnd();});
	    return false;
		},
	drag : function(e)
		{
		var x = parseInt(flyBox.root.style.left)+ e.clientX - flyBox.lastMouseX;
		var y = parseInt(flyBox.root.style.top)+ e.clientY - flyBox.lastMouseY;
		flyBox.lastMouseX  = e.clientX;
		flyBox.lastMouseY  = e.clientY;
		flyBox.root.style.left = (x<0?0:x) + "px";
		flyBox.root.style.top = (y<0?0:y) + "px";
		return false;
		},
	dragEnd : function()
		{
	    $(flyBox.root).css('opacity',1);
	    $(document).unbind('mousemove');
	    $(document).unbind('mouseup');
	    flyBox.obj = null;
		}
};
function flyTab(o,className)
	{
	$('.'+className).removeClass('selected');
	$(o).addClass('selected');
	$('.'+className+'c').hide();
	$('#'+$(o).attr('id')+'c').show();
	return false;
	}
var flyMenu = {
	what:null,
	show: function(event,y,x,what,me)
		{
		event = jQuery.event.fix( event || window.event || {} );
		if(!me) me=event.target;
		if(flyMenu.what!=null)
			{
			if(flyMenu.what==what) return flyMenu.close();
			flyMenu.close();
			}
		flyMenu.what=(what?what:'#flymenu');
	    var coors = findPos(me);
	    $(flyMenu.what).css({top:coors[1]+y,left:coors[0]+x}).show();

		event.preventDefault();
		event.stopPropagation();
		$(document).bind("click", function(){ flyMenu.close(); });

		return false;
		},
	close:function()
		{
		$(flyMenu.what).hide();
		$(document).unbind("click");
		flyMenu.what=null;
		return false;
		}
};

var flyTip = {
	what:null,
    x:0,
    y:0,
	show: function(event,s,what,x,y,ref)
		{
		flyTip.over(event,s,what,x,y,ref);
		},
	hide: function()
		{
		flyTip.out();
		},
	over: function(event,s,what,x,y,obj)
		{
		event = jQuery.event.fix( event || window.event || {} );
		flyTip.what=(what?what:'#flytip');
		flyTip.x=(x?x:flyTip.x);
		flyTip.y=(y?y:flyTip.y);
        offSetY=(obj?(flyTip.findPos(obj))['y']-z:event.pageY);
        offSetX=(obj?(flyTip.findPos(obj))['x']:event.pageX);
		if(s.substr(0,1)=="#") s=$(s).html();
		$(flyTip.what).html(s).css({top:offSetY+20+flyTip.y,left:offSetX+flyTip.x}).show();
		return false;
		},
    findPos: function(obj)
        {
        var curleft = curtop = 0;
        if (obj.offsetParent)
            {
            do
                {
                curleft += obj.offsetLeft;
                curtop += obj.offsetTop;
                } while (obj = obj.offsetParent);
            }
        return {x:curleft, y:curtop};
        },
	move:function(event)
		{
		event = jQuery.event.fix( event || window.event || {} );
		$(flyTip.what).css({top:event.pageY+20+flyTip.y,left:event.pageX+flyTip.x}).show();
		},
	out:function(timeout)
		{
		$(flyTip.what).hide();
		}
};

/*** other helper functions */
function findPos(obj)
	{
	var curleft = curtop = 0;
	if(obj.offsetParent)
		{
	    curleft = obj.offsetLeft;
	    curtop = obj.offsetTop;
	    while (obj = obj.offsetParent)
			{
	        curleft += obj.offsetLeft;
	        curtop += obj.offsetTop;
		    }
		}
	return [curleft,curtop];
	}
function checkemail(s)
	{
	if(s.substr(0,1)=="#") s=$(s).val();
	var filter=/^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i
	return filter.test(s);
	}
var wpop;
function popup(p,w,h,s,r)
	{
	if(wpop) try {wpop.window.close();} catch(e) {}
	s=s||'yes';
	r=r||'yes';
	var t=(screen.height-h)/2;
	var l=(screen.width-w)/2;
	wpop=window.open(p, "Popup", "toolbar=no, location=no, directories=no, status=no, scrollbars="+s+", resizable="+r+", copyhistory=no, width="+w+", height="+h+", left="+l+", top="+t+"");
	wpop.focus();
	return false;
	}

;(function($) {

$.fn.extend({
	autocomplete: function(urlOrData, options) {
		var isUrl = typeof urlOrData == "string";
		options = $.extend({}, $.Autocompleter.defaults, {
			url: isUrl ? urlOrData : null,
			data: isUrl ? null : urlOrData,
			delay: isUrl ? $.Autocompleter.defaults.delay : 10,
			max: options && !options.scroll ? 10 : 150
		}, options);

		// if highlight is set to false, replace it with a do-nothing function
		options.highlight = options.highlight || function(value) { return value; };

		// if the formatMatch option is not specified, then use formatItem for backwards compatibility
		options.formatMatch = options.formatMatch || options.formatItem;

		return this.each(function() {
			new $.Autocompleter(this, options);
		});
	},
	result: function(handler) {
		return this.bind("result", handler);
	},
	search: function(handler) {
		return this.trigger("search", [handler]);
	},
	flushCache: function() {
		return this.trigger("flushCache");
	},
	setOptions: function(options){
		return this.trigger("setOptions", [options]);
	},
	unautocomplete: function() {
		return this.trigger("unautocomplete");
	}
});

$.Autocompleter = function(input, options) {

	var KEY = {
		UP: 38,
		DOWN: 40,
		DEL: 46,
		TAB: 9,
		RETURN: 13,
		ESC: 27,
		COMMA: 188,
		PAGEUP: 33,
		PAGEDOWN: 34,
		BACKSPACE: 8
	};

	// Create $ object for input element
	var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);

	var timeout;
	var previousValue = "";
	var cache = $.Autocompleter.Cache(options);
	var hasFocus = 0;
	var lastKeyPressCode;
	var config = {
		mouseDownOnSelect: false
	};
	var select = $.Autocompleter.Select(options, input, selectCurrent, config);

	var blockSubmit;

	// prevent form submit in opera when selecting with return key
	$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
		if (blockSubmit) {
			blockSubmit = false;
			return false;
		}
	});

	// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
	$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
		// track last key pressed
		lastKeyPressCode = event.keyCode;
		switch(event.keyCode) {

			case KEY.UP:
				event.preventDefault();
				if ( select.visible() ) {
					select.prev();
				} else {
					onChange(0, true);
				}
				break;

			case KEY.DOWN:
				event.preventDefault();
				if ( select.visible() ) {
					select.next();
				} else {
					onChange(0, true);
				}
				break;

			case KEY.PAGEUP:
				event.preventDefault();
				if ( select.visible() ) {
					select.pageUp();
				} else {
					onChange(0, true);
				}
				break;

			case KEY.PAGEDOWN:
				event.preventDefault();
				if ( select.visible() ) {
					select.pageDown();
				} else {
					onChange(0, true);
				}
				break;

			// matches also semicolon
			case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
			case KEY.TAB:
			case KEY.RETURN:
				if( selectCurrent() ) {
					// stop default to prevent a form submit, Opera needs special handling
					event.preventDefault();
					blockSubmit = true;
					return false;
				}
				break;

			case KEY.ESC:
				select.hide();
				break;

			default:
				clearTimeout(timeout);
				timeout = setTimeout(onChange, options.delay);
				break;
		}
	}).focus(function(){
		// track whether the field has focus, we shouldn't process any
		// results if the field no longer has focus
		hasFocus++;
	}).blur(function() {
		hasFocus = 0;
		if (!config.mouseDownOnSelect) {
			hideResults();
		}
	}).click(function() {
		// show select when clicking in a focused field
		if ( hasFocus++ > 1 && !select.visible() ) {
			onChange(0, true);
		}
	}).bind("search", function() {
		// TODO why not just specifying both arguments?
		var fn = (arguments.length > 1) ? arguments[1] : null;
		function findValueCallback(q, data) {
			var result;
			if( data && data.length ) {
				for (var i=0; i < data.length; i++) {
					if( data[i].result.toLowerCase() == q.toLowerCase() ) {
						result = data[i];
						break;
					}
				}
			}
			if( typeof fn == "function" ) fn(result);
			else $input.trigger("result", result && [result.data, result.value]);
		}
		$.each(trimWords($input.val()), function(i, value) {
			request(value, findValueCallback, findValueCallback);
		});
	}).bind("flushCache", function() {
		cache.flush();
	}).bind("setOptions", function() {
		$.extend(options, arguments[1]);
		// if we've updated the data, repopulate
		if ( "data" in arguments[1] )
			cache.populate();
	}).bind("unautocomplete", function() {
		select.unbind();
		$input.unbind();
		$(input.form).unbind(".autocomplete");
	});


	function selectCurrent() {
		var selected = select.selected();
		if( !selected )
			return false;

		var v = selected.result;
		previousValue = v;

		if ( options.multiple ) {
			var words = trimWords($input.val());
			if ( words.length > 1 ) {
				v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
			}
			v += options.multipleSeparator;
		}

		$input.val(v);
		hideResultsNow();
		$input.trigger("result", [selected.data, selected.value]);
		return true;
	}

	function onChange(crap, skipPrevCheck) {
		if( lastKeyPressCode == KEY.DEL ) {
			select.hide();
			return;
		}

		var currentValue = $input.val();

		if ( !skipPrevCheck && currentValue == previousValue )
			return;

		previousValue = currentValue;

		currentValue = lastWord(currentValue);
		if ( currentValue.length >= options.minChars) {
			$input.addClass(options.loadingClass);
			if (!options.matchCase)
				currentValue = currentValue.toLowerCase();
			request(currentValue, receiveData, hideResultsNow);
		} else {
			stopLoading();
			select.hide();
		}
	};

	function trimWords(value) {
		if ( !value ) {
			return [""];
		}
		var words = value.split( options.multipleSeparator );
		var result = [];
		$.each(words, function(i, value) {
			if ( $.trim(value) )
				result[i] = $.trim(value);
		});
		return result;
	}

	function lastWord(value) {
		if ( !options.multiple )
			return value;
		var words = trimWords(value);
		return words[words.length - 1];
	}

	// fills in the input box w/the first match (assumed to be the best match)
	// q: the term entered
	// sValue: the first matching result
	function autoFill(q, sValue){
		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
		// if the last user key pressed was backspace, don't autofill
		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
			// fill in the value (keep the case the user has typed)
			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
			// select the portion of the value not typed by the user (so the next character will erase)
			$.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
		}
	};

	function hideResults() {
		clearTimeout(timeout);
		timeout = setTimeout(hideResultsNow, 200);
	};

	function hideResultsNow() {
		var wasVisible = select.visible();
		select.hide();
		clearTimeout(timeout);
		stopLoading();
		if (options.mustMatch) {
			// call search and run callback
			$input.search(
				function (result){
					// if no value found, clear the input box
					if( !result ) {
						if (options.multiple) {
							var words = trimWords($input.val()).slice(0, -1);
							$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
						}
						else
							$input.val( "" );
					}
				}
			);
		}
		if (wasVisible)
			// position cursor at end of input field
			$.Autocompleter.Selection(input, input.value.length, input.value.length);
	};

	function receiveData(q, data) {
		if ( data && data.length && hasFocus ) {
			stopLoading();
			select.display(data, q);
			autoFill(q, data[0].value);
			select.show();
		} else {
			hideResultsNow();
		}
	};

	function request(term, success, failure) {
		if (!options.matchCase)
			term = term.toLowerCase();
		var data = cache.load(term);
		// recieve the cached data
		if (data && data.length) {
			success(term, data);
		// if an AJAX url has been supplied, try loading the data now
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){

			var extraParams = {
				timestamp: +new Date()
			};
			$.each(options.extraParams, function(key, param) {
				extraParams[key] = typeof param == "function" ? param() : param;
			});

			$.ajax({
				// try to leverage ajaxQueue plugin to abort previous requests
				mode: "abort",
				// limit abortion to this input
				port: "autocomplete" + input.name,
				dataType: options.dataType,
				url: options.url,
				data: $.extend({
					q: lastWord(term),
					limit: options.max
				}, extraParams),
				success: function(data) {
					var parsed = options.parse && options.parse(data) || parse(data);
					cache.add(term, parsed);
					success(term, parsed);
				}
			});
		} else {
			// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
			select.emptyList();
			failure(term);
		}
	};

	function parse(data) {
		var parsed = [];
		var rows = data.split("\n");
		for (var i=0; i < rows.length; i++) {
			var row = $.trim(rows[i]);
			if (row) {
				row = row.split("|");
				parsed[parsed.length] = {
					data: row,
					value: row[0],
					result: options.formatResult && options.formatResult(row, row[0]) || row[0]
				};
			}
		}
		return parsed;
	};

	function stopLoading() {
		$input.removeClass(options.loadingClass);
	};

};

$.Autocompleter.defaults = {
	inputClass: "ac_input",
	resultsClass: "ac_results",
	loadingClass: "",
	minChars: 1,
	delay: 400,
	matchCase: false,
	matchSubset: true,
	matchContains: false,
	cacheLength: 10,
	max: 100,
	mustMatch: false,
	extraParams: {},
	selectFirst: true,
	formatItem: function(row) { return row[0]; },
	formatMatch: null,
	autoFill: true,
	width: 0,
	multiple: false,
	multipleSeparator: ", ",
	highlight: function(value, term) {
		return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
	},
    scroll: true,
    scrollHeight: 180
};

$.Autocompleter.Cache = function(options) {

	var data = {};
	var length = 0;

	function matchSubset(s, sub) {
		if (!options.matchCase)
			s = s.toLowerCase();
		var i = s.indexOf(sub);
		if (i == -1) return false;
		return i == 0 || options.matchContains;
	};

	function add(q, value) {
		if (length > options.cacheLength){
			flush();
		}
		if (!data[q]){
			length++;
		}
		data[q] = value;
	}

	function populate(){
		if( !options.data ) return false;
		// track the matches
		var stMatchSets = {},
			nullData = 0;

		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
		if( !options.url ) options.cacheLength = 1;

		// track all options for minChars = 0
		stMatchSets[""] = [];

		// loop through the array and create a lookup structure
		for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
			var rawValue = options.data[i];
			// if rawValue is a string, make an array otherwise just reference the array
			rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;

			var value = options.formatMatch(rawValue, i+1, options.data.length);
			if ( value === false )
				continue;

			var firstChar = value.charAt(0).toLowerCase();
			// if no lookup array for this character exists, look it up now
			if( !stMatchSets[firstChar] )
				stMatchSets[firstChar] = [];

			// if the match is a string
			var row = {
				value: value,
				data: rawValue,
				result: options.formatResult && options.formatResult(rawValue) || value
			};

			// push the current match into the set list
			stMatchSets[firstChar].push(row);

			// keep track of minChars zero items
			if ( nullData++ < options.max ) {
				stMatchSets[""].push(row);
			}
		};

		// add the data items to the cache
		$.each(stMatchSets, function(i, value) {
			// increase the cache size
			options.cacheLength++;
			// add to the cache
			add(i, value);
		});
	}

	// populate any existing data
	setTimeout(populate, 25);

	function flush(){
		data = {};
		length = 0;
	}

	return {
		flush: flush,
		add: add,
		populate: populate,
		load: function(q) {
			if (!options.cacheLength || !length)
				return null;
			/*
			 * if dealing w/local data and matchContains than we must make sure
			 * to loop through all the data collections looking for matches
			 */
			if( !options.url && options.matchContains ){
				// track all matches
				var csub = [];
				// loop through all the data grids for matches
				for( var k in data ){
					// don't search through the stMatchSets[""] (minChars: 0) cache
					// this prevents duplicates
					if( k.length > 0 ){
						var c = data[k];
						$.each(c, function(i, x) {
							// if we've got a match, add it to the array
							if (matchSubset(x.value, q)) {
								csub.push(x);
							}
						});
					}
				}
				return csub;
			} else
			// if the exact item exists, use it
			if (data[q]){
				return data[q];
			} else
			if (options.matchSubset) {
				for (var i = q.length - 1; i >= options.minChars; i--) {
					var c = data[q.substr(0, i)];
					if (c) {
						var csub = [];
						$.each(c, function(i, x) {
							if (matchSubset(x.value, q)) {
								csub[csub.length] = x;
							}
						});
						return csub;
					}
				}
			}
			return null;
		}
	};
};

$.Autocompleter.Select = function (options, input, select, config) {
	var CLASSES = {
		ACTIVE: "ac_over"
	};

	var listItems,
		active = -1,
		data,
		term = "",
		needsInit = true,
		element,
		list;

	// Create results
	function init() {
		if (!needsInit)
			return;
		element = $("<div/>")
		.hide()
		.addClass(options.resultsClass)
		.css("position", "absolute")
		.appendTo(document.body);

		list = $("<ul/>").appendTo(element).mouseover( function(event) {
			if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
	            active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
			    $(target(event)).addClass(CLASSES.ACTIVE);
	        }
		}).click(function(event) {
			$(target(event)).addClass(CLASSES.ACTIVE);
			select();
			// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
			input.focus();
			return false;
		}).mousedown(function() {
			config.mouseDownOnSelect = true;
		}).mouseup(function() {
			config.mouseDownOnSelect = false;
		});

		if( options.width > 0 )
			element.css("width", options.width);

		needsInit = false;
	}

	function target(event) {
		var element = event.target;
		while(element && element.tagName != "LI")
			element = element.parentNode;
		// more fun with IE, sometimes event.target is empty, just ignore it then
		if(!element)
			return [];
		return element;
	}

	function moveSelect(step) {
		listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
		movePosition(step);
        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
        if(options.scroll) {
            var offset = 0;
            listItems.slice(0, active).each(function() {
				offset += this.offsetHeight;
			});
            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
            } else if(offset < list.scrollTop()) {
                list.scrollTop(offset);
            }
        }
	};

	function movePosition(step) {
		active += step;
		if (active < 0) {
			active = listItems.size() - 1;
		} else if (active >= listItems.size()) {
			active = 0;
		}
	}

	function limitNumberOfItems(available) {
		return options.max && options.max < available
			? options.max
			: available;
	}

	function fillList() {
		list.empty();
		var max = limitNumberOfItems(data.length);
		for (var i=0; i < max; i++) {
			if (!data[i])
				continue;
			var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
			if ( formatted === false )
				continue;
			var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
			$.data(li, "ac_data", data[i]);
		}
		listItems = list.find("li");
		if ( options.selectFirst ) {
			listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
			active = 0;
		}
		// apply bgiframe if available
		if ( $.fn.bgiframe )
			list.bgiframe();
	}

	return {
		display: function(d, q) {
			init();
			data = d;
			term = q;
			fillList();
		},
		next: function() {
			moveSelect(1);
		},
		prev: function() {
			moveSelect(-1);
		},
		pageUp: function() {
			if (active != 0 && active - 8 < 0) {
				moveSelect( -active );
			} else {
				moveSelect(-8);
			}
		},
		pageDown: function() {
			if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
				moveSelect( listItems.size() - 1 - active );
			} else {
				moveSelect(8);
			}
		},
		hide: function() {
			element && element.hide();
			listItems && listItems.removeClass(CLASSES.ACTIVE);
			active = -1;
		},
		visible : function() {
			return element && element.is(":visible");
		},
		current: function() {
			return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
		},
		show: function() {
			var offset = $(input).offset();
			element.css({
				width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
				top: offset.top + input.offsetHeight,
				left: offset.left
			}).show();
            if(options.scroll) {
                list.scrollTop(0);
                list.css({
					maxHeight: options.scrollHeight,
					overflow: 'auto'
				});

                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
					var listHeight = 0;
					listItems.each(function() {
						listHeight += this.offsetHeight;
					});
					var scrollbarsVisible = listHeight > options.scrollHeight;
                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
					if (!scrollbarsVisible) {
						// IE doesn't recalculate width when scrollbar disappears
						listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
					}
                }

            }
		},
		selected: function() {
			var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
			return selected && selected.length && $.data(selected[0], "ac_data");
		},
		emptyList: function (){
			list && list.empty();
		},
		unbind: function() {
			element && element.remove();
		}
	};
};

$.Autocompleter.Selection = function(field, start, end) {
	if( field.createTextRange ){
		var selRange = field.createTextRange();
		selRange.collapse(true);
		selRange.moveStart("character", start);
		selRange.moveEnd("character", end);
		selRange.select();
	} else if( field.setSelectionRange ){
		field.setSelectionRange(start, end);
	} else {
		if( field.selectionStart ){
			field.selectionStart = start;
			field.selectionEnd = end;
		}
	}
	field.focus();
};

})(jQuery);

