var searchDir = "/search/";
var searchURL = searchDir + 'searchfeed/searchfeeder.cfm';

function htmlDecode(text){
    return $('<div/>').html(text).text();
}

function submitSearch(){
    document.location.href= searchDir + 'thorsearch.cfm?search='+$('#criteria').val();
}

function formatItem(item){
    var resultsFound = item.PNBR + ' - ' + item.PN;
    var regEx =  new RegExp("("+$('#criteria').val()+")","ig");
    resultsFound = htmlDecode(resultsFound);
    resultsFound = resultsFound.replace(regEx, '<strong>$1</strong>');
    return '<div class="resultsNode' + ((item.OFFSET%2 == 0)?' rowStripe':'') + '"><div><img src="'+item.TN+'" /><a href="' +item.PAGELINK+ '&pn='+item.PNBR+'">' + resultsFound + '</a></div></div>';
}

function formatTerm(term){
    var resultsFound = term.OUTPUT;
    var regEx =  new RegExp("("+$('#criteria').val()+")","ig");
    resultsFound = htmlDecode(resultsFound);
    resultsFound = resultsFound.replace(regEx, '<strong>$1</strong>');
    return '<div class="resultsNode' + ((term.OFFSET%2 == 0)?' rowStripe':'') + '"><div><a href="'+ searchDir + 'thorsearch.cfm?search='+term.OUTPUT+'">' + resultsFound + '</a></div></div>';
}

function clearSelections(){
    $("#results div.resultsNode.selected").removeClass("selected");
}

function positionSearchSuggest(searchField, dropdown){
    // Use width: (criteria.outerWidth() + "px") to use a dynamic width
    var theWidth = 433;

    //Set the width of the drop down first, because we need to calculate outerWidth later.
    dropdown.css({width: (theWidth + 'px')}); 

    //Figure out just how wide the search field is, taking into account its borders
    var adjustedWidth = (searchField.outerWidth() > searchField.width()) ? searchField.outerWidth() : searchField.width();

    //For the left property, set it to be flush right with the field if it can't fit onto the screen flush left.
    dropdown.css({
        left: (($(window).width() > (searchField.offset().left + dropdown.outerWidth())) ? (searchField.offset().left + 'px') : (searchField.offset().left - (dropdown.outerWidth() - adjustedWidth) + "px"))
        , top: (searchField.offset().top + searchField.outerHeight() + "px")
        , width: (theWidth + "px")
    });


}

// Apply the given AJAX response to the container.
function applyListItems(container, targetArea, response){

    //Get all of the search terms
    try{
        var terms = response.T;
    }
    catch(err){
        //If we couldn't get the terms for whatever reason, set them to an empty array to prevent errors.
        var terms = [];
    }
    // Create an array to hold our HTML buffer
    var htmlBuffer = [];

    //Clear the search term div
    $("#terms").html('');
    if(terms.length){
        $.each(terms, function(index, term){htmlBuffer.push(formatTerm(term));});
        $('#terms').fadeIn().html('<h3>Keyword Suggestions</h3>' + htmlBuffer.join(""));
    }
    else{
        $('#terms').fadeOut();  
        $('#terms').hide();     
    }

    //Get all of the list items to parse through
    var htmlBuffer = [];
    try{
        var items = response.P;
    }
    catch(err){
        //If we couldn't get the products for whatever reason, set them to an empty array to prevent errors.
        var items = [];
    }

    if(items.length == 0){
        if(container.children().length == 0){
            $('#productsWrapper').fadeOut();
            $('#productsWrapper').hide();       
        }
    }
    else{
        // Loop over the array to create each list element.
        $.each(items, function(index, item){htmlBuffer.push(formatItem(item));});

        // Create a list chunk which will hold our data.
        var chunk = $("<div class='list-chunk'></div>");
        chunk.append(htmlBuffer.join(""));

        // Create the min and max offset of the chunk.
        chunk.data("minOffset", items[ 0 ].OFFSET);
        chunk.data("maxOffset", items[ items.length - 1 ].OFFSET);

        // Append list items.
        container.append(chunk);
        
        //Fade in the products
        $('#productsWrapper').fadeIn();

        //Set the offsets for later use.
        container.data("minOffset", container.children(":first").data("minOffset"));
        container.data("maxOffset", container.children(":last").data("maxOffset"));
    }
}

// Check to see if more list items are needed, and, if they are, load them.
function checkListItemContents(container){
    // Returns: { top: boolean, bottom: boolean }.
    var isMoreLoading = isMoreListItemsNeeded(container);
    var onComplete = function(){return true;};

    // Check to see if more list items are needed at the
    // top. If so, we will check to offsets to see if the
    // load needs to take place.
    //
    // NOTE: Position is only *part* of how we determine
    // if additional content is needed at the top.
    if(isMoreLoading.top && container.data("minOffset") && (container.data("minOffset") > 1)){
        // Load and prepend more list items.
        getMoreListItems(container,"top",onComplete);

    // Check to see if more list items are needed at the
    // bottom. For this, all we are going to rely on is
    // the offset of the container (since we can load
    // ad-infinitum in the bottom direction).
    }
    else if(isMoreLoading.bottom){
        // Load and append more list items.
        getMoreListItems(container,"bottom",onComplete);
    }
}

// Check to see if more list items are needed based on
// the scroll offset of the window and the position of
// the container. Return a complex result that not only
// determines IF more list items are needed, but on what
// end of the list.
//
// NOTE: These calculate are based ONLY on the offset of
// the list container in the context of the view frame.
// This does not take anything else into account (more
// business logic might be required to see if loading
// truly needs to take place).
function isMoreListItemsNeeded(container){
    // Create a default return. This return value contains
    // requirements for both the top and bottom of the
    // content list.
    var result = {
        top: false,
        bottom: false
    };

    // Get the view frame for the window - this is the
    // top and bottom coordinates of the visible slice of
    // the document.
    var viewTop = container.scrollTop();
    var viewBottom = (viewTop + $(container).height());

    // Get the offset of the top of the list container.
    //var containerTop = container.offset().top;
    var containerTop = 0;

    // Get the offset of the bottom of the list container.
    var containerBottom = Math.floor(
        containerTop + $(container).attr('scrollHeight')
    );

    // Scroll buffers for the top and the bottom;
    // this is the amount of pre-top and pre-bottom space
    // we want to take into account before we start
    // loading the next items.
    //
    // NOTE: The top buffer is a bit bigger only to make
    // the transition feel a bit *safer*.
    var topScrollBuffer = 50;
    var bottomScrollBuffer = 40;

    // Check to see if the container top is close enough
    // (with buffer) to the top scroll of the view frame
    // to trigger loading more items (at the top).
    if((containerTop + topScrollBuffer) >= viewTop){
        // Flag requirement at top.
        result.top = true;
    }

    // Check to see if the container bottom is close
    // enought (with buffer) to the scroll of the view
    // frame to trigger loading more items (at the
    // bottom).
    if((containerBottom - bottomScrollBuffer) <= viewBottom){
        // Flag requirement at bottom.
        result.bottom = true;
    }

// Return the requirments for the loading.
return(result);
}

// Get more list items and either prepend them or append
// them to the list depending on the target area.
function getMoreListItems(container, targetArea, onComplete){
    if($('#criteria').val().length < 3){return false;}
    // Check to see if there is any existing AJAX call
    // for the list data items. If there is, we want to
    // return out of this method - no reason to overload
    // the server with extraneous requests (more so than
    // an infinite scroll effect already does!!).
    if(container.data("xhr")){
        // Let the active AJAX request complete.
        return;
    }
    // Get the min and max offsets of the current container.
    var minOffset = (container.data("minOffset") || 0);
    var maxOffset = (container.data("maxOffset") || 0);

    // The count of list items to load per AJAX request.
    // We are calling it a "chunk" size because each
    // list chunk will be stored in its own sub-container
    // to make DOM manipulation easier.
    var chunkSize = 10;

    // Check our target area to see what our next offset for loading should be.
    if(targetArea == "top"){
        // We are prepending list items.
        var nextOffset = (minOffset - 1 - chunkSize);
    }
    else{
        // We are appending list items.
        var nextOffset = (maxOffset + 1);
    }

    // Launch AJAX request for next set of results and
    // store the resultant XHR request with the container.
    container.data(
        "xhr",
        $.ajax({
            type: "get",
            url: searchURL,
            data: {
                offset: nextOffset
                , q: $('#criteria').val()
                , _cf_clientid: _cf_clientid //CAN BE SET BY CFAJAXIMPORT 
            },
            dataType: "json",
            success: function(response){
                // Apply the response to the container
                // for the given target area.
                applyListItems(container, targetArea, response);
            },
            complete: function(){
                // Remove the stored AJAX request. This
                // will allow subsequent AJAX requests
                // to execute.
                container.removeData("xhr");

                // Call the onComplete callback.
                onComplete();
                }

            })

        );

    }


// When the DOM is ready, initialize script.
$().ready(function() {  
    // Get references to our main DOM items.
    var criteria = $("#criteria");
    var results = $("#results");
    var products = $("#products");

    // Resize and position the results element to be below
    // the criteria input (for this demo, we're not going
    // to worry about any window resizing issues).

    //Position the dropdown 
    positionSearchSuggest(criteria, results);

    var leaveTimer;
    //When to hide the menu:
    results.live('mouseleave', function(){
        //Set a timer to fade out after half a second, so we can get back into the menu if we overshoot it.
        leaveTimer = setTimeout(function(){
            results.fadeOut();
        }, 1500);
    });
    
    //Prevent the blur of the input from hiding the menu when the mouse is over the menu.
    results.live('mouseover', function(){
        //Clear the leaveTimer so that the menu doesn't close since we made it back over in time.
        clearTimeout(leaveTimer);
        criteria.unbind('blur');
    });
    
    //Dealing with selections
    $('div.resultsNode').live('mouseover', function(){
        clearSelections();
        $(this).addClass('selected');
    });
    $('div.resultsNode').live('mouseout', function(){
        $(this).removeClass('selected');
    });

    criteria.bind('focus', function(){
        //Set the text color to black
        
        //Hide the menu when you leave the input
        criteria.bind('blur', function(){
        results.fadeOut();
        });
    });
    

    // Turn off auto-complete on the criteria input.
    criteria.attr("autocomplete", "off");

    // Bind the scroll and resize events to the window.
    // Whenever the user scrolls or resizes the window,
    // we will need to check to see if more list items
    // need to be loaded.
    products.bind(
        "scroll"
        , function(){
            // Hand the control-flow off to the method
            // that worries about the list content.
            checkListItemContents(products);
        }
    );
    
    //Deal with click events on the divs. Go to the first found href inside the div.
    $('#products div.resultsNode div').live('click', function(){
        document.location.href = $(this).find('a')[0].href;
    });
    $('#terms div').live('click', function(){
        document.location.href = $(this).find('a')[0].href;
    });

    // This will keep track of the AJAX request object so that we can abort it if necessary.
    var xhr = null;

    // This will keep track of the key-down timer so that we don't launch too many requests to the server.
    var resultsTimer = null;
    
    // Get the initial results from the server for the auto-suggest.
    var getResults = function(query){

        if(query.length < 3){return false;}
        // Get the results.
        xhr = $.ajax({
            type: "get",
            url: searchURL,
            data: {
                offset: 0
                , q: query
                , _cf_clientid: _cf_clientid //CAN BE SET BY CFAJAXIMPORT 
            },
            dataType: "json",
            success: function(response){
                // Populate the results.
                $('#products').html('');
                applyListItems(products, 'bottom', response);

                try{//Show the results if we had either products or terms, else hide

                    (response.P.length || response.T.length)? results.show() : results.hide();

                }catch(err){
                    //If anything bad happens, then hide the results
                    results.hide();
                }

            }

        });

    };

    // Bind the keyup events in the input.
    criteria.keyup(
        function(event){
            // Clear any existing timer.
            clearTimeout(resultsTimer);
            
            // Clear any existing request.
            if(xhr){
                xhr.abort();
            }

            var keyPressed = "";

            //Reposition the search field just in case the browser changed width
            positionSearchSuggest($('#criteria'), $("#results"))

            switch (event.keyCode) {
                case 13:
                    // Perform some action when enter is placed
                    keyPressed="Enter!";
                    var goTo = $("#results div.resultsNode.selected").find('a')[0];
                    if(typeof(goTo) != 'undefined'){//If the user has an item highlighted in the list, use that link
                        document.location.href = goTo.href;
                    }
                    else{
                        this.form.submit();
                    }
                    return false;
                    break;

                case 27:
                    keyPressed = "ESC";
                    $('#results').fadeOut();
                    return false;
                    break; 
                
                case 38:
                    // Navigate up
                    keyPressed="Up";
                    break;

                case 40:
                    // Navigate down
                    keyPressed="Down";
                    $('#results').fadeIn();
                    break;  
                
                //Ignore these keys completely
                case 14://Shift In
                case 15://Shift Out
                case 16://Shift
                case 17://Ctrl
                case 18://Alt/Option
                case 19://Pause/Break
                case 20://Caps Lock
                case 32://Space bar
                case 35://End
                case 36://Home
                case 37://Left arrow
                case 39://Right arrow
                case 45://Insert
                case 91://Windows Keys
                case 92://Windows Keys
                case 224://Command
                    return false;
                    break;
            }
            
            if(keyPressed == "Down" || keyPressed == "Up"){
                //Determine which row to highlight  
                var cur_idx = $("#results div.resultsNode").index($(".selected"));
                var max_idx = $("#results div.resultsNode").length - 1;
                
                if(keyPressed == "Down"){
                    var next_idx = (cur_idx >= max_idx)? max_idx : cur_idx + 1;
                }
                else{//It's up
                    var next_idx = (cur_idx > 0)? cur_idx - 1 : 0;
                }

                if(typeof next_idx != 'undefined') {
                    //Mark the proper items as selected
                    clearSelections();
                    $("#results div.resultsNode").eq(next_idx).addClass("selected");
                    
                    //Check to see if the node is visible or not
                    var newSelection = $("#results div.resultsNode.selected");
                    if(!newSelection.parent().parent().length){
                        return false;
                    }
                    //If the new selection is inside the products, then we need to figure out if we need to scroll 
                    //to show the newly selected item in the Products list
                    if(newSelection.parent().parent()[0].id == 'products'){
                        var container = $('#products');
                        var selectionTop = newSelection.position().top - container.position().top;

                        //If we hit the bottom
                        if(selectionTop+ newSelection.height() > container.height()){
                            container.scrollTop(container.scrollTop() + newSelection.outerHeight() + (selectionTop - container.height()));

                        }
                        else if(selectionTop < 0){
                            //We hit the top
                            container.scrollTop(container.scrollTop() - newSelection.height() + selectionTop);

                        }

                    }

                }

            }
            else{
                //Something other than up, down, enter, or esc was pressed.
                // Get the current query value.
                var query = criteria.val();

                if(query.length > 0){
                    // Set a timer to get the next set of results.
                    resultsTimer = setTimeout(function(){getResults(query);}, 500);
                }

            } //end check for up or down key press

        } //end keyup function

    );

});
