/*
     *  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("ul"));
        var $form = $("form");

        var timeout = false;        // hold timeout ID for suggestion results to appear 
        var prevLength = 0;         // last recorded length of $input.val()
        var cache = [];             // cache MRU list
        var cacheSize = 0;          // size of cache in chars (bytes?)
            
        $results.addClass(options.resultsClass).appendTo('body');

        resetPosition();
        $(window)
        .load(resetPosition)        // just in case user is changing size of page while loading
        .resize(resetPosition);

        $input.blur(function() {
            setTimeout(function() { 
                $results.hide() 
            }, 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();
            $results.css({
                top: (offset.top + input.offsetHeight) + 'px',
                left: offset.left + '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 q = $.trim($input.val());
            if (q.length >= options.minchars && q.match(/^[\w\s]*$/)) {
                cached = checkCache(q);
                if (cached) {
                    displayItems(cached['items']);
                } else {
                    // This is a workaround for using the suggest in the editor. The position is only known
                    // when the DIV is shown and the user is typing
                    resetPosition();
                    $.get(options.source, {
                        query: q,
                        indexNames: options.indexNames,
                        indexTypes: options.indexTypes,
                        superQuery: options.query
                    }, function(txt) {
                        $results.hide();
                        var items = parseTxt(txt, q);
                        displayItems(items);
                        addToCache(q, items, txt.length);
                    });
                }
            } else {
                $results.hide();
            }
        }
            
        function checkCache(q) {
            for (var i = 0; i < cache.length; i++)
                if (cache[i]['q'] == q) {
                    cache.unshift(cache.splice(i, 1)[0]);
                    return cache[0];
                }
            return false;
        }
            
        function addToCache(q, items, size) {
            while (cache.length && (cacheSize + size > options.maxCacheSize)) {
                var cached = cache.pop();
                cacheSize -= cached['size'];
            }
                
            cache.push({
                q: q,
                size: size,
                items: items
            });
                    
            cacheSize += size;
        }
            
        function displayItems(items) {
            if (!items)
                return;
            if (!items.length) {
                $results.hide();
                return;
            }
                
            var html = '';
            for (var i = 0; i < items.length; i++)
                html += '<li>' + items[i] + '</li>';

            $results.html(html).show();
            $results
            .children('li')
            .mouseover(function() {
                $results.children('li').removeClass(options.selectClass);
                $(this).addClass(options.selectClass);
            })
            .click(function(e) {
                e.preventDefault(); 
                e.stopPropagation();
                selectCurrentResult();
            });
        }
            
        function parseTxt(txt, q) {
            var items = [];
            var tokens = eval(txt);
                
            // parse returned data for non-empty items
            for (var i = 0; i < tokens.length; i++) {
                var token = $.trim(tokens[i]);
                if (token) {
                    var surrounds = token.split(/\">.*?</);
                    var content = token.match(/\">.*?</);
                    content = content[0].substring(2, content[0].length-1);
                    token = content.replace(
                        new RegExp(q, 'ig'), 
                        function(q) { 
                            return '<span class="' + options.matchClass + '">' + q + '</span>' 
                        }
                        );
                    items[items.length] = surrounds[0] + '">' + token + '<' + surrounds[1];
                }
            }
            return items;
        }
            
        function getCurrentResult() {
            if (!$results.is(':visible'))
                return false;
            
            var $currentResult = $results.children('li.' + options.selectClass);
                
            if (!$currentResult.length)
                $currentResult = false;
            return $currentResult;

        }
            
        function selectCurrentResult() {
            $currentResult = getCurrentResult();
            if ($currentResult) {
                $input.val(options.getValue($currentResult));
                
                if (options.displayValueName.length > 0) {
                    var escapedId = options.displayValueName.replace(/(:|\.)/g,'\\$1');
                    if($("#" + escapedId).length) {
                        console.log(options.displayValueName);      
                        $("#" + escapedId).attr('value',options.getDisplayValue($currentResult));
                    }
                    else {
                        $('<input  type="hidden" id="' + options.displayValueName + '" name="' + options.displayValueName + '" value="' + options.getDisplayValue($currentResult) + '"/>').appendTo($form);
                    }       
                }        
                if (options.referenceId.length > 0) {
                    var id = '#'+options.referenceId;
                    // this is needed for escaping in jquery selector API
                    if (id.indexOf('.') > -1) {
                        var splitId = id.split(/\./);
                        id = splitId[0];
                        for(i = 1; i < splitId.length; i++){
                            id = id + '\\.' + splitId[i];
                        }
                    }
                    jQuery(id).attr("value", options.getReferenceId($currentResult)); 
                }         
                
                $results.hide();
                if (options.createLabels == true) {
					$labelContainer = $(options.labelContainer);
					// Check label doesn't already exist					
					if ($("div#ac_label_" + options.labelContext + "_" +  options.getReferenceId($currentResult)).length == 0) {
						// console.log("Label doesn't exist");
						// Create a new label 
						
						$labelStyling = " style='display:inline; margin-left: 0.25em; margin-right: 0.25em'";
						$label = "<div id='ac_label_" + options.labelContext + "_" + options.getReferenceId($currentResult) + "' class='ac_label_" + options.labelContext + "' " + $labelStyling + ">" + options.getReferenceId($currentResult) + "</div>";
						
						//console.log("#ac_label_" + options.labelContext + "_" +  options.getReferenceId($currentResult));
						$removeLabelLink = " (<a id='ac_remove_label_" + options.labelContext + "_" + options.getReferenceId($currentResult) + "' class='ac_remove_label_link' href='#'>X</a>)";
						
						// Append the new label to the list of labels
						$labelContainer.append($label);
						$jqLabel = $("#ac_label_" + options.labelContext + "_" + options.getReferenceId($currentResult));
						
						$($jqLabel).append($removeLabelLink);
						resetPosition();
						$jqRemoveLabelLink = $("#ac_remove_label_" + options.labelContext + "_" + options.getReferenceId($currentResult));
						$($jqRemoveLabelLink).bind("click", function(){
							$(this).parents("div.ac_label_" + options.labelContext).fadeOut(250, function(){
								$(this).remove();
								resetPosition();
							});
							return false;
						});
						$input.val("");
					}
                    else {
                       // console.log("Label exists");
                       $("div#ac_label_" + options.labelContext + "_" +  options.getReferenceId($currentResult)).effect("highlight", {}, 1000).css("display","inline");
                    }
                }
                
                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('li:first-child').addClass(options.selectClass);
        }
            
        function prevResult() {
            $currentResult = getCurrentResult();
            if ($currentResult)
                $currentResult
                .removeClass(options.selectClass)
                .prev()
                .addClass(options.selectClass);
            else
                $results.children('li:last-child').addClass(options.selectClass);
        }
    }
        
    $.fn.suggest = function(source, options) {
        if (!source)
            return;
        options = options || {};
        options.source = source;
        options.delay = options.delay || 300;
        options.resultsClass = options.resultsClass || 'ac_results';
        options.selectClass = options.selectClass || 'ac_over';
        options.matchClass = options.matchClass || 'ac_match';
        options.minchars = options.minchars || 2;
        options.onSelect = options.onSelect || false;
        options.maxCacheSize = options.maxCacheSize || 65536;
        options.indexTypes = options.indexTypes || 'form';
        options.query = options.query || '';
        options.indexNames = options.indexNames || 'page';
        options.displayValueName = options.displayValueName || '';
        options.referenceId = options.referenceId || '';
        options.getValue = options.getValue || function (currentResult) {
            var firstIndex = 0;
            var lastIndex = 0;
            if (options.referenceId.length > 0) {
                lastIndex = currentResult.text().lastIndexOf('(') - 1;
            } else {
                firstIndex = currentResult.text().lastIndexOf('(') + 1;
                lastIndex = currentResult.text().lastIndexOf(')');
            }
            return currentResult.text().substring(firstIndex, lastIndex);
        };
        options.getDisplayValue = options.getDisplayValue || function (currentResult) {
            var index = currentResult.text().lastIndexOf('(');
            return currentResult.text().substring(0, index);
        };
        options.getReferenceId = options.getReferenceId || function (currentResult) {
            var start = currentResult.text().lastIndexOf('(') + 1;
            var end = currentResult.text().lastIndexOf(')');
            return currentResult.text().substring(start, end);
        };
    
        options.createLabels = options.createLabels || false;
		options.labelContext = options.labelContext || "";
        options.labelContainer = options.labelContainer || "";
    
        this.each(function() {
            new $.suggest(this, options);
        });
    
        return this;
    };
})(jQuery);