/*
* jQuery Autocomplete plugin 1.1
*
* Copyright (c) 2009 Jörn Zaefferer
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*
* Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
*/;

(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 );
									options.highlight=options.highlight || function(value)
										{
											return value;
										};
									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};
				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;
				$.browser.opera&&$( input.form ).bind( "submit.autocomplete", function()
					{
						if (blockSubmit)
							{
								blockSubmit=false;
								return false;
							}
					} );
				$input.bind( ($.browser.opera ?"keypress": "keydown") + ".autocomplete", function(event)
								{
									hasFocus=1;
									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;
											case options.multiple && $.trim( options.multipleSeparator ) == "," && KEY.COMMA:
											case KEY.TAB:
											case KEY.RETURN:
												if (selectCurrent( ))
													{
														event.preventDefault( );
														blockSubmit=true;
														return false;
													}
												break;
											case KEY.ESC:
												select.hide( );
												break;
											default:
												clearTimeout( timeout );
												timeout=setTimeout( onChange, options.delay );
												break;
										}
								} ).focus( function()
					{
						hasFocus++;
					} ).blur( function()
					{
						hasFocus=0;
						if (!config.mouseDownOnSelect)
							{
								hideResults( );
							}
					} ).click( function()
					{
						if (hasFocus++ > 1 && !select.visible( ))
							{
								onChange( 0, true );
							}
					} ).bind( "search", function()
					{
						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 ("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)
									{
										var seperator = options.multipleSeparator.length;
										var cursorAt = $( input ).selection( ).start;
										var wordAt,progress=0;
										$.each( words, function(i, word)
											{
												progress+=word.length;
												if (cursorAt <= progress)
													{
														wordAt=i;
														return false;
													}
												progress+=seperator;
											} );
										words[ wordAt ]=v;
										v=words.join( options.multipleSeparator );
									}
								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[""];
							if (!options.multiple)return[$.trim( value )];
						return $.map( value.split( options.multipleSeparator ), function(word)
							{
								return $.trim( value ).length ?$.trim( word ): null;
							} );
					}
				function lastWord(value)
					{
						if (!options.multiple)return value;
						var words = trimWords( value );
						if (words.length == 1)return words[ 0 ];
						var cursorAt = $( input ).selection( ).start;
						if (cursorAt == value.length)
							{
								words=trimWords( value )
							}
						else
							{
								words=trimWords( value.replace( value.substring( cursorAt ), "" ) );
							}
						return words[ words.length - 1 ];
					}
				function autoFill(q, sValue)
					{
						if (options.autoFill && (lastWord( $input.val( ) ).toLowerCase( ) == q.toLowerCase( )) && lastKeyPressCode != KEY.BACKSPACE)
							{
								$input.val( $input.val( ) + sValue.substring( lastWord( previousValue ).length ) );
								$( input ).selection( 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)
							{
								$input.search( function(result)
									{
										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( "" );
														$input.trigger( "result", null );
													}
											}
									} );
							}
					};
				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 );
						if (data && data.length)
							{
								success( term, data );
							}
						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( {mode:"abort",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
							{
								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:"ac_loading",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:false,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 (options.matchContains == "word")
							{
								i=s.toLowerCase( ).search( "\\b" + sub.toLowerCase( ) );
							}
						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;
							var stMatchSets = {},nullData=0;
						if (!options.url)options.cacheLength=1;
							stMatchSets[ "" ]=[];
						for (var i = 0,ol=options.data.length;i < ol;i++)
							{
								var rawValue = options.data[ i ];
									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 (!stMatchSets[ firstChar ])stMatchSets[ firstChar ]=[];
									var row = {value:value,data:rawValue,result:options.formatResult && options.formatResult( rawValue ) || value};
								stMatchSets[ firstChar ].push( row );
								if (nullData++ < options.max)
									{
										stMatchSets[ "" ].push( row );
									}
							}
						;
						$.each( stMatchSets, function(i, value)
							{
								options.cacheLength++;
								add( i, value );
							} );
					}
				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 (!options.url && options.matchContains)
								{
										var csub = [];
									for (var k in data)
										{
											if (k.length > 0)
												{
													var c = data[ k ];
													$.each( c, function(i, x)
														{
															if (matchSubset( x.value, q ))
																{
																	csub.push( x );
																}
														} );
												}
										}
									return csub;
								}
							else
								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;
				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( );
								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;
							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;
							}
						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)
													{
														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( );
							}};
			};
		$.fn.selection=function(start, end)
			{
				if (start !== undefined)
					{
						return this.each( function()
							{
								if (this.createTextRange)
									{
										var selRange = this.createTextRange( );
										if (end === undefined || start == end)
											{
												selRange.move( "character", start );
												selRange.select( );
											}
										else
											{
												selRange.collapse( true );
												selRange.moveStart( "character", start );
												selRange.moveEnd( "character", end );
												selRange.select( );
											}
									}
								else if (this.setSelectionRange)
									{
										this.setSelectionRange( start, end );
									}
								else if (this.selectionStart)
									{
										this.selectionStart=start;
										this.selectionEnd=end;
									}
							} );
					}
				var field = this[ 0 ];
				if (field.createTextRange)
					{
						var range = document.selection.createRange( ),orig=field.value,teststring="<->",textLength=range.text.length;
						range.text=teststring;
						var caretAt = field.value.indexOf( teststring );
						field.value=orig;
						this.selection( caretAt, caretAt + textLength );
							return{start:caretAt,end:caretAt + textLength}
					}
				else if (field.selectionStart !== undefined)
					{
						return{start:field.selectionStart,end:field.selectionEnd}
					}
			};
	})( jQuery );
