// Based on http://www.otto.de/is-bin/intershop.static/WFS/Otto-OttoDe-Site/-/de_DE/js/jquery/jquery.suggest.otto.js

/*
	 *	based on jquery.suggest 1.1 - 2007-08-06
	 *	
	 *	modified for loading elements by jsonp from fredhopper suggest server
	 *
	 */
 	
	/*
	 *	jquery.suggest 1.1 - 2007-08-06
	 *	
	 *	Uses code and techniques from following libraries:
	 *	1. http://www.dyve.net/jquery/?autocomplete
	 *	2. http://dev.jquery.com/browser/trunk/plugins/interface/iautocompleter.js	
	 *
	 *	All the new stuff written by Peter Vulgaris (www.vulgarisoip.com)	
	 *	Feel free to do whatever you want with this file
	 *
	 */
	
	(function($) {

		$.suggest = function(input, options) {
	
			var $input = $(input).attr("autocomplete", "off");
			var $results = $(document.createElement("div"));
			

			var timeout = false;		// hold timeout ID for suggestion results to
                              // appear
			var prevLength = 0;			// last recorded length of $input.val()
			var prevScopeValue = null;		// last recorded scope value
			var cache = [];				// cache MRU list
			var cacheSize = 0;			// size of cache in chars (bytes?)
			
			var q = "";                 // query from input field
			var inputHasFocus = true;   // storage for focus information
			
			$results.addClass(options.resultsClass).appendTo('body');

			resetPosition();
			$(window)
				.load(resetPosition)		// just in case user is changing size of page
                                // while loading
				.resize(resetPosition);

			$input.focus(function() {
				inputHasFocus = true;
			});

			$input.blur(function() {
				setTimeout(function() { $results.hide(); inputHasFocus = false; }, 200);
			});
			
			
			// help IE users if possible
			try {
				$results.bgiframe();
			} catch(e) { }


		// I really hate browser detection, but I don't see any other way
      if ($.browser.mozilla)
        $input.keypress(processKey);  // onkeypress repeats arrow keys in Mozilla/Opera
      else
        $input.keydown(processKey);   // onkeydown repeats arrow keys in IE/Safari


			function resetPosition() {
			// requires jquery.dimension plugin
        var offset = $input.offset();
        
        if ($.browser.msie && $.browser.version < 7) {
            $results.css({
                top: (offset.top + input.offsetHeight + 12) + 'px',
                left: (offset.left - 11) + 'px'
            });
        }else
        {
            $results.css({
                top: (offset.top + input.offsetHeight + 12) + 'px',
                left: (offset.left - 14) + 'px'
            });
        }

			}
			
			
			function processKey(e) {
        
        // handling up/down/escape requires results to be visible
        // handling enter/tab requires that AND a result to be selected
        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: // up
              prevResult();
              break;
        
            case 40: // down
              nextResult();
              break;
  
            case 9:  // tab
            case 13: // return
              selectCurrentResult();
              break;
              
            case 27: // escape
              $results.hide();
              break;
  
          }
          
        } else if ($input.val().length != prevLength) {

          if (timeout) 
            clearTimeout(timeout);
          timeout = setTimeout(suggest, options.delay);
          prevLength = $input.val().length;
          
        }     
          
        
      }

			
			
			function suggest() {

				var scopeValue = '';
				if (options.scopeID){
					var scopeObj = $("#"+options.scopeID); 
					if ( scopeObj[0] && scopeObj[0].value ){
						scopeValue = scopeObj[0].value;
						if ( scopeValue == options.scopeValueToSkip){
							// do nothing, if given element has a certain value
							return;
						} 
					}
				}
				
				if ($input.val().length == prevLength && (prevScopeValue == null || prevScopeValue == scopeValue )) {
				    // no operation, if nothing has changed...
					return;
				}
				
				// store current values:
				prevLength = $input.val().length;
				prevScopeValue = scopeValue;
				
				var q = $.trim($input.val());

				if (q.length >= options.minchars) {

					cached = checkCache(q, scopeValue);
					
					if (cached) {
					
						displayItems(cached['items']);
						
					} else {
					    
					    var scope_cat = '';
					    if (scopeValue.length >0){
					    	scope_cat = "/scope_cat>{" + encodeURIComponent(scopeValue) + "}";
					    }
						$.ajax( {
							dataType: "json",
							data: "q=" + encodeURIComponent(q),
							url: options.source,
							scriptCharset: options.scriptCharset,
							success: function(txt){

								$results.hide();
							
								// do nothing, if no results provided:
								if (!txt || !txt.suggestions){
									return;
								}
							
								var items = parseTxt(txt, q);
							
								displayItems(items);
								addToCache(q, items, txt.length, scopeValue);
							
							}
						});
						
					}
					
				} else {
				
					$results.hide();
					
				}
					
			}
			
			
			function checkCache(q, scope) {

				if(!scope){
					scope = '';
				}

				for (var i = 0; i < cache.length; i++)
					if (cache[i]['q'] == q && cache[i]['scope'] == scope) {
						cache.unshift(cache.splice(i, 1)[0]);
						return cache[0];
					}
				
				return false;
			
			}
			
			function addToCache(q, items, size, scope) {

				while (cache.length && (cacheSize + size > options.maxCacheSize)) {
					var cached = cache.pop();
					cacheSize -= cached['size'];
				}
				
				if(!scope){
					scope = '';
				}
				
				cache.push({
					q: q,
					scope: scope, 
					size: size,
					items: items
					});
					
				cacheSize += size;
			
			}
			
			function displayItems(items) {
				
				if (!items)
					return;
					
				if (!items.length) {
					$results.hide();
					return;
				}
				
				var html = '';
				if (options.showHint){
					html += "<div class='top'><span>" + options.hintText + "</span></div>";
				}
				html += '<ul>';
				for (var i = 0; i < items.length; i++) {
					html += '<li suggestion="' + toHtml(items[i][0]) + '">' + items[i][1] + '</li>';
				}
				html += '</ul>';
				$results.html(html);
				$results.show();
				
				$results
					.find('li')
					.mouseover(function() {
						$results.find('li').removeClass(options.selectClass);
						$(this).addClass(options.selectClass);
					})
					.click(function(e) {
						e.preventDefault(); 
						e.stopPropagation();
						selectCurrentResult();
					});
							
			}

			function toHtml(str) {
				var elem = document.createElement('span');
				elem.appendChild(document.createTextNode(str));
				return elem.innerHTML;
			}

			function parseTxt(txt, q) {
				
				// two dimensional array: one entry for the suggestion as plain text,
        // the other one with layout and additional info
				var items = [];

				for (var sugNr = 0; sugNr < txt.suggestions.length; sugNr++) {
					var suggestion = txt.suggestions[sugNr];
					var searchterm = suggestion.query;
					
					var index = items.length;
					items[items.length] = [ searchterm, undefined ];
					var length = searchterm.length;
					var suggestionNrResults = "";
					
					if (suggestion.resultCount){
						suggestionNrResults = '' + suggestion.resultCount; 
					}
					
					if (options.showResultsCount){
						length = length + suggestionNrResults.length;
					}
					
					if (length > options.maxLength){
					    // cut off and replace with maxLengthReplacement
						searchterm = searchterm.substring(0, options.maxLength - suggestionNrResults.length - options.maxLengthReplacement.length) + options.maxLengthReplacement;
					}
					searchterm = toHtml(searchterm).replace(
						new RegExp(q, 'ig'), 
						function(q) { return '<span class="' + options.matchClass + '">' + toHtml(q) + '</span>' }
					);

					// items[index][1] holds suggestion as displayed to user
					if (options.showResultsCount && suggestionNrResults.length > 0){
						items[index][1] = searchterm + "<span class='" + options.countClass + "'> (" + suggestionNrResults + ")</span>";
					} else {
						items[index][1] = searchterm;
					}
				}
				return items;
			}
			
			function getCurrentResult() {
			
				if (!$results.is(':visible'))
					return false;
			
				var $currentResult = $results.find('li.' + options.selectClass);
				
				if (!$currentResult.length)
					$currentResult = false;
					
				return $currentResult;

			}
			
			function selectCurrentResult() {
			
				$currentResult = getCurrentResult();
			
				if ($currentResult) {

					$input.val($currentResult.attr("suggestion"));
					$results.hide();
					
					$input.closest("#searchbox").submit();
					if (options.onSelect){
						options.onSelect.apply($input[0]);
					}
				}
			
			}
			
			function nextResult() {
	      
        $currentResult = getCurrentResult();
      
        if ($currentResult)
          $currentResult
            .removeClass(options.selectClass)
            .next()
              .addClass(options.selectClass);
        else
          $results.children('ul').children('li:first-child').addClass(options.selectClass);
      
      }
      
      function prevResult() {
      
        $currentResult = getCurrentResult();
      
        if ($currentResult)
          $currentResult
            .removeClass(options.selectClass)
            .prev()
              .addClass(options.selectClass);
        else
          $results.children('ul').children('li:last-child').addClass(options.selectClass);
      
      }
	
		}
		
		$.fn.suggest = function(source, options) {
		
			if (!source)
				return;
		
			options = options || {};
			options.source = source;
			options.delay = options.delay || 100;
			options.resultsClass = options.resultsClass || 'ac_results';
			options.selectClass = options.selectClass || 'ac_over';
			options.matchClass = options.matchClass || 'ac_match';
			options.countClass = options.countClass || 'ac_count';
			options.hintClass = options.hintClass || 'ac_hint';
			options.minchars = options.minchars || 1;
			options.delimiter = options.delimiter || '\n';
			options.onSelect = options.onSelect || false;
			options.maxCacheSize = options.maxCacheSize || 65536;
			options.maxLength = options.maxLength || 22; // maximum length of the
                                                    // displayed suggestion
                                                    // including the optional
                                                    // display of counted
                                                    // results
														 // (if longer, strip off and replace with
                              // maxLengthReplacement)
			options.maxLengthReplacement = options.maxLengthReplacement || '...'; // strip
                                                                            // off
                                                                            // suggestion
                                                                            // and
                                                                            // replace
                                                                            // with
                                                                            // this
                                                                            // string
                                                                            // if
                                                                            // longer
                                                                            // than
                                                                            // maxLength
			options.showResultsCount = options.showResultsCount || (true && !options.showResultsCount); // show
                                                                                                  // number
                                                                                                  // of
                                                                                                  // results
                                                                                                  // after
                                                                                                  // suggestion
			options.showHint = options.showHint || (true && !options.showHint); // display
                                                                          // hint
                                                                          // (on/off)
			options.hintText = options.hintText || 'Suchvorschl&auml;ge';
			options.scopeID = options.scopeID; // id of element which value holds an
                                          // additional search scope
			options.scopeValueToSkip = options.scopeValueToSkip; // if this value is
                                                            // present, do not
                                                            // process
                                                            // suggestions
			options.scriptCharset = options.scriptCharset || 'UTF-8'; // Character Set
                                                                // of the
                                                                // Suggest
                                                                // Server
	
			this.each(function() {
				new $.suggest(this, options);
			});
	
			return this;
			
		};
		
	})(jQuery);

