(function($){
    $.suggest = function(input, input_hidden, options) {
		if (typeof (options.showSearchBar) == "undefined") {
			options.showSearchBar = true;
		}
		if (typeof (options.showCode) == "undefined") {
			options.showCode = true;
		}
		var $input = $(input);
		var $input_hidden = $(input_hidden);
        if ($input.attr("hassuggest") == "true") {  return false; }		

		var timeout = false;
        var prevLength = 0;
        var cache = [];
        var cacheSize = 0;
		var listMode = 0; //default list mode
		
		$input.attr("autocomplete", "off");
        $input.attr("hassuggest", "true");
		
        input.suggestObject = this;
        $input.val("");                

		var $resultsOptions = $(document.createElement("DIV"));		
		if (options.showSearchBar) {
        $resultsOptions.html(options.searchBar).addClass(options.resultsOptClass).appendTo('body');
		$resultsOptions.find("a:eq(0)").click(function(){ setListMode(1); $input.focus(); displayItems(options.source); });
		$resultsOptions.find("a:eq(1)").click(function(){ setListMode(0); $input.focus(); displayItems(options.source); });
		} else {
			$resultsOptions.addClass(options.resultsOptClass).appendTo('body');
		}
		
		//$resultsOptions.find("a:eq(0)").click(function(){ setListMode(1); $input.focus(); suggest(); });
		//$resultsOptions.find("a:eq(1)").click(function(){ setListMode(0); $input.focus(); suggest(); });
		
		var $results = $(document.createElement("DIV"));		
        $results.addClass(options.resultsClass).appendTo('body');
		if (jQuery.browser.msie&&jQuery.browser.version<7) {				
			$results.before("<div class='ac_iframe'>&nbsp;</div>");
		}
		var $iframe = $results.prev(".ac_iframe");
		
		setListMode(0);
		
        resetPosition();
        $(window).resize(hide);
        $(document).click(switchResults);
		
        $input.keyup(processKey);
		$input.keydown(setTabEvent);        
		$input.mousedown(function(){$(this).css("background-position","right bottom")}).mouseup(function(){$(this).css("background-position","right top")});
        
		function setListMode (mode) {
			listMode=mode; 
			var items = options.source;			
			if (listMode == 0) { //list by city
				items.sort(function(a, b) {return strCompareTo(a[1],b[1]);});				
			}
			
			if (listMode == 1) { //list by country
				items.sort(function(a, b) {
				    var ac=(a[2]==options.isRussia)?" ":a[2];
				    var bc=(b[2]==options.isRussia)?" ":b[2];
					var c=strCompareTo(ac,bc);
					if (c==0) {
						c=strCompareTo(a[1],b[1]);
					}
					return c;			
				});
			}
			options.source = items;
		}
		
        function resetPosition(){
		//alert(options.showSearchBar);
			if (jQuery.boxModel) {
				if (!options.showSearchBar) {
				$results.css({
					//width: ($input.outerWidth()-2) + 'px',
						top: ($input.offset().top + $input.outerHeight() +1) + 'px',
						left: ($input.offset().left) + 'px'
					});
				} else {
					$results.css({
					top: ($input.offset().top + $input.outerHeight() +34+1) + 'px',
					left: ($input.offset().left) + 'px'
				});
				}
			} else {
				if (!options.showSearchBar) {
					$results.css({
						//width: ($input.outerWidth() - 1) + 'px',
						top: ($input.offset().top + $input.innerHeight() +2) + 'px',
						left: ($input.offset().left - 2) + 'px'
					});
			} else {
				$results.css({
					//width: ($input.outerWidth() - 1) + 'px',
					top: ($input.offset().top + $input.innerHeight() +34+2) + 'px',
					left: ($input.offset().left - 2) + 'px'
				});
			}
			}
			$resultsOptions.css( {
				//width:($input.outerWidth() - 1) + 'px',
				top:($input.offset().top + $input.outerHeight() +1) + 'px',
				left:($input.offset().left) + 'px'
			});
            
			var numberOfChildren = $results.children().length;
            var height = numberOfChildren == 0 ? 0 : 192;
            height = (numberOfChildren <= 10 && numberOfChildren > 0) ? ((numberOfChildren * 19) + "px") : height;
            if (height != "0") {
                $results.css("height", height)
            }
        }
		
        function show(){
            if ($input.get(0).disabled) return;
            resetPosition();		 	            
			$results.show();
			$results.children().removeClass(options.selectClass);
			$resultsOptions.show();
			/*if (!options.showSearchBar) {
				$iframe.css("width",($results.width()+2)+"px").css("height",($results.height()+0+2)+"px").css("top",$resultsOptions.offset().top+"px").css("left",$resultsOptions.offset().left+"px").show().bgiframe();
			} else {*/
			$iframe.css("width",($results.width()+2)+"px").css("height",($results.height()+34+2)+"px").css("top",$resultsOptions.offset().top+"px").css("left",$resultsOptions.offset().left+"px").show().bgiframe();
			//}
        }
		
        function hide(){
            $results.hide(options.showDelay);
			$resultsOptions.hide()
			$iframe.hide();
			
        }
		        
        function setTabEvent(e){
            if ((e.keyCode == "9") && $results.is(':visible')) {
                hide();
            }
						if ($results.children(".item").length==1) { // Checking if only one result (CR)													
							nextResult();
						}
        }
		
        function processKey(e){
            if ((/27$|38$|40$/.test(e.keyCode) && $results.is(':visible')) || (/^13$|^9$/.test(e.keyCode) && getCurrentResult())) {
                if (e.preventDefault) e.preventDefault();
                if (e.stopPropagation) e.stopPropagation();
                e.cancelBubble = true;
                e.returnValue = false;
                switch (e.keyCode) {
                    case 38:
                        prevResult();
                        break;
                    case 40:
                        nextResult();
                        break;
                    case 9:
                    case 13:
                        selectCurrentResult(  );						
                        break;
                    case 27:
                        hide();
                        break;
                }
            }
            else 
			if ($input.val().length != prevLength) {
				if (timeout) 
					clearTimeout(timeout);
				timeout = setTimeout(suggest, options.delay);
				prevLength = $input.val().length;
			}
        }
		
        function switchResults(e){
           	//if (e.preventDefault) e.preventDefault();
           	//if (e.stopPropagation) e.stopPropagation();
            $target = $(e.target);
            if ($target.attr('id') == $input.attr('id') ) {
                processInputClick(e)
            }
            else if ($target.hasClass('suggest-control')) {                
				return false;															  
            } else  {
				hide();
			}			
        }
		
        function processInputClick(e){
			/*if ($results.is(':visible')) {
				setTimeout(function(){
					hide();
				}, 200);
			}
			else {
				suggest();
			}*/
            var clickZone = $input.position().left + $input.get(0).clientWidth - e.pageX;
            if (clickZone <= 17) {
                if ($results.is(':visible')) {
                    setTimeout(function(){
                        hide();
                    }, 200);
                }
                else {
                    displayItems(options.source);
                }
            }
            else {
                if ($results.is(':visible')) {
                    setTimeout(function(){
                        hide();
                    }, 200);
                }
                else {
                    suggest();
                }
            }  
        }
		
        function suggest() {
            var q = $.trim($input.val());
			q = ((q.indexOf("(")>-1)&&(q.indexOf(")")>-1))?q.substr(q.indexOf("(")+1,3):q;
			//q = escape(q);
            if (q.length >= options.minchars) {
                hide();				
                var items = filterResults(options.source, q);
                displayItems(items);
            }
            else {
                displayItems(options.source);
            }
        }
		
		function strCompareTo(a,b) {
			if (a<b) {
				return -1;
			} else if (a>b) {
				return 1;
			} else {
				return 0;
			}
		}
		
        function displayItems(items){
            if (!items) return;
            if (!items.length) {  
				hide();
                return;
            }
			
			setListMode(listMode);
            var html = '';
			
			if (listMode == 0) { 
				for (var i = 0; i < items.length; i++) {					
					html += '<div class="item"><div class="left">' + items[i][1];
					html += (options.showCode) ? ' (' + items[i][0] + ') ' : '';
					html += '</div><div class="right">' + items[i][2] + '</div><div class="clear">&nbsp;</div></div>';
				}
			}
			if (listMode == 1) { 
				var curCountry = "";
				for (var i = 0; i < items.length; i++) {
					if (items[i][2]!=curCountry) {
						html += '<div class="country">' + items[i][2] + '</div>';
						curCountry = items[i][2];
					}
					html += '<div class="item"><div class="left-2">' + items[i][1]  + ' (' + items[i][0] + ') ' + '</div></div>';
				}
			}
			
            $results.html(html);			
            show();
            $results.children('div.item').mouseover(function(e){
				if (!$(this).hasClass(options.selectClass)) {
	                $(this).addClass(options.selectClass);
				}
            }).mouseout(function(){				
				$(this).removeClass(options.selectClass);								
			}).click(function(e){
                if (e.preventDefault) e.preventDefault();
                if (e.stopPropagation) e.stopPropagation();
                selectCurrentResult();
            });
            scrollResults(0);
        }
		
		this.getExtIndex = function (){
			return  $input_hidden.val();
		} 
		
        function filterResults(txt, q){
			var items = [];			
            var tokens = txt;
			q=q.toUpperCase();
			q = q.replace(/([\*\.\+\?\/\^\?\\\!\|\[\]\{\}\(\)])/g, "");
            //q = q.replace(/([\*\.\+\?\/\^\?\\\!\|\[\]\{\}\(\)])/g, "\\$1").replace(/%u04([0-9ABCDEF]{2})/g, "\\u04$1");	
            for (var i = 0; i < tokens.length; i++) {
                var t1 = tokens[i][1]; //city
                var t2 = tokens[i][0]; //code
                var t3 = tokens[i][2]; //country
		var codeMatch = (options.showCode) ? t2.match(new RegExp(q, 'ig')) : false;
				
                if (t1.match(new RegExp(q, 'ig')) || codeMatch || t3.match(new RegExp(q, 'ig'))) {
                    t1 = t1.replace(new RegExp(q, 'ig'), function(q){
                        return '<span class="' + options.matchClass + '">' + q + '</span>'
                    });
                    t2 = t2.replace(new RegExp(q, 'ig'), function(q){
                        return '<span class="' + options.matchClass + '">' + q + '</span>'
                    });
                    t3 = t3.replace(new RegExp(q, 'ig'), function(q){
                        return '<span class="' + options.matchClass + '">' + q + '</span>'
                    });
   					items.push(new Array(t2,t1,t3));
                }				
            }
            if (items.length == 0) {
                items.push = '<span class="' + options.noResultsClass + '">' + options.noResults + '</span>';
            }
            return items;
        }
		
        function getCurrentResult(){
			
            if (!$results.is(':visible')) return false;
            var $currentResult = $results.children('.' + options.selectClass);
            if (!$currentResult.length) $currentResult = false;
            return $currentResult;
        }
		
		function getLocByCode(code,map) {
			for (var i=0;i<map.length;i++){
				if (map[i][0]==code) return map[i];
			}
		}
		function getLocByName(name,map) {
			for (var i=0;i<map.length;i++){
				if (map[i][1]==name) return map[i];
			}
		}
		
        function selectCurrentResult(_ctrl){
            var $currentResult = getCurrentResult();
            if ($currentResult.length>0) {              
				var txt = $currentResult.text();
/* previous version
				var t1 = txt.substring(txt.lastIndexOf('(')+1, txt.lastIndexOf(')'));
				var loc = getLocByCode(t1,options.source)
				var t2 = loc[1]+" ("+loc[0]+") "+loc[2];
*/
				var t1, t2, loc;
				if (options.showCode) {
					t1 = txt.substring(txt.lastIndexOf('(')+1, txt.lastIndexOf(')'));
					loc = getLocByCode(t1,options.source);
					t2 = loc[1]+" ("+loc[0]+") "+loc[2];
				} else {
					loc = getLocByName(txt.trim(),options.source);
					t1 = loc[0];
					t2 = loc[1]+" "+loc[2];
				}
				$input.val(t2);
				$input_hidden.val(t1);
				$input_hidden.change();
			}				
			hide();
			if (options.onSelect) {
				options.onSelect.apply($input[0]);
			}			
			if (jQuery.browser.msie) {
				var _input = $input.get(0);
				while (_input.scrollWidth>=(_input.offsetWidth)) {
					var _val = $input.val();
					$input.val(_val.substring(0, _val.length-1));
				}
				setTimeout(function(){input.focus()},100);		
            }		
        }
        
        function nextResult(){			
            var $currentResult = $(getCurrentResult()).removeClass(options.selectClass);
			var next = $currentResult.nextAll("div.item:first");
			next = (next.length>0)?next:$results.children('div.item:first');            
			next.addClass(options.selectClass);
            scrollResults($results.children("div").index(next));
        }
		
        function prevResult(){
			var $currentResult = $(getCurrentResult()).removeClass(options.selectClass);
			var prev = $currentResult.prevAll("div.item:first");
			prev = (prev.length>0)?prev:$results.children('div.item:last');            
			prev.addClass(options.selectClass);
            scrollResults($results.children("div").index(prev));
        }
		
        function scrollResults(_index){
            var _height = $results[0].scrollHeight;
            var total = $results.children('div').length;
            var _scrollTop = parseInt((_height * _index) / total);
            $results.scrollTop(_scrollTop - ($results.height() / 2));
        }
		
        this.setSource = function(source){
            options.source = source;            
        }		

	this.setFirstResult = function(){
		$input.val("")
		suggest();
		nextResult();
		selectCurrentResult();	
        }		

    };
	
    $.fn.suggest = function(source, input_hidden, options) {
        var code;
        if (!source) return this;
        options = options || {};
        options.source = source;
        options.delay = options.delay || 100;
		options.searchBar = options.searchBar || '';
        options.resultsClass = options.resultsClass || 'ac_results';
		options.resultsOptClass = options.resultsOptClass || 'ac_options suggest-control';
        options.selectClass = options.selectClass || 'ac_over';
        options.matchClass = options.matchClass || 'ac_match';
        options.noResultsClass = options.noResultsClass || 'ac_noresults';
        options.minchars = options.minchars || 1;
		options.buttondown = options.buttondown || "../images/buttons/dropdwonarrow-down.gif";
		options.buttonup = options.buttonup || "../images/buttons/dropdwonarrow-up.gif";
        options.delimiter = options.delimiter || ',';
        options.onSelect = options.onSelect || false;
		options.isRussia = options.isRussia || "Russian Federation";
		options.linkedControl = options.linkedControl || false;
        options.noResults = options.noresults || 'No results found';
        options.maxCacheSize = options.maxCacheSize || 65536;
        options.showDelay = options.showDelay || 0;
        this.each(function(){
            new $.suggest(this, input_hidden, options);
        });
        return this;
    };
})(jQuery);
