// Original script: http://ajaxphp.packtpub.com/ajax/suggest/

var suggestData = [];
var currentField;

// constructor for suggest data
function Data (keywordId, idId) {
    
    this.oKeyword = document.getElementById(keywordId);
    this.oId = document.getElementById(idId);
    
    /* the keyword for which an HTTP request has been initiated */
    this.httpRequestKeyword = this.oKeyword.value;
    /* the last keyword for which suggests have been requested */
    this.userKeyword = this.oKeyword.value;
    /* number of suggestions received as results for the keyword */
    this.suggestionCount = 0;
    /* the maximum number of characters to be displayed for a suggestion */
    this.suggestionMaxLength = 60;
    /* flag that indicates if the up or down arrow keys were pressed
   the last time a keyup event occurred  */
    this.isKeyUpDownPressed = false;
    /* the last suggestion that has been used for autocompleting the keyword */
    this.autocompletedKeyword = "";
    /* flag that indicates if there are results for the current requested keyword*/
    this.hasResults = false;
    /* the identifier used to cancel the evaluation with the clearTimeout method. */
    this.timeoutId = -1;
    /* the currently selected suggestion (by arrow keys or mouse)*/
    this.position = -1;
    /* cache object containing the retrieved suggestions for different keywords */
    this.oCache = new Object();
    /* the minimum and maximum position of the visible suggestions */
    this.minVisiblePosition = 0;
    this.maxVisiblePosition = 9;
    this.currentSuggestions = null;
    
    this.addToCache = addToCache;
    this.checkCache = checkCache;
    this.getSuggestions = getSuggestions;
    this.selectRange = selectRange;
    this.deselectAll = deselectAll;
    this.autocompleteKeyword = autocompleteKeyword;
    this.hideSuggestions = hideSuggestions;
    this.displayResults = displayResults;
    this.clear = function () {this.oKeyword.value = ''; this.oId.value = 0}
}

/* function that initializes the page */
function initSuggest(elementId, idParamId)
{
    newData = new Data(elementId, idParamId);
    
    // prevent browser from starting the autofill function
    newData.oKeyword.setAttribute("autocomplete", "off");
    
    suggestData[elementId] = newData;
    setTimeout("checkForChanges('"+elementId+"')", 500);
}

/* function that adds to a keyword an array of values */
function addToCache(keyword, values)
{
    // create a new array entry in the cache
    this.oCache[keyword] = new Array();
    // add all the values to the keyword's entry in the cache
    for(i=0; i<values.length; i++) {
        this.oCache[keyword][i] = values[i];
    }
}

/*
   function that checks to see if the keyword specified as parameter is in
   the cache or tries to find the longest matching prefixes in the cache
   and adds them in the cache for the current keyword parameter
 */
function checkCache(keyword)
{
    // check to see if the keyword is already in the cache
    if(this.oCache[keyword])
        return true;
    // try to find the biggest prefixes
    for(i=keyword.length-2; i>=0; i--)
    {
        // compute the current prefix keyword
        var currentKeyword = keyword.substring(0, i+1);
        // check to see if we have the current prefix keyword in the cache
        if(this.oCache[currentKeyword])
        {
            // the current keyword's results already in the cache
            var cacheResults = this.oCache[currentKeyword];
            // the results matching the keyword in the current cache results
            var keywordResults = new Array();
            var keywordResultsSize = 0;
            // try to find all matching results starting with the current prefix
            for(j=0;j<cacheResults.length;j++)
            {
                if(cacheResults[j].label.indexOf(keyword) == 0)
                    keywordResults[keywordResultsSize++] = cacheResults[j];
            }
            // add all the keyword's prefix results to the cache
            this.addToCache(keyword, keywordResults);
            return true;
        }
    }
    // no match found
    return false;
}

/* initiate HTTP request to retrieve suggestions for the current keyword */
function getSuggestions(keyword)
{
    /* continue if keyword isn't null and the last pressed key wasn't up or
     down */
    if(keyword != "" && !this.isKeyUpDownPressed)
    {
        // check to see if the keyword is in the cache
        isInCache = this.checkCache(keyword);
        this.userKeyword=keyword;
        this.httpRequestKeyword=keyword;
        // if keyword is in cache...
        if(isInCache == true)
        {
            // display the results in the cache
            this.displayResults(keyword, this.oCache[keyword]);
        }
        // if the keyword isn't in cache, make an HTTP request
        else
        {
            jsonrpc.nszForm.suggest(updateSuggestions, keyword, this.oKeyword.id);
        }
    }
}


/* function that processes the server's response */
function updateSuggestions(result, exception)
{
    if (result) {
        var keywordId = result.map.keywordId;
        var currentContext = suggestData[keywordId];
        // initialize the new array of functions' names
        nameArray = result.map.values.list;
        // check to see if we have any results for the searched keyword

        // check to see if other keywords are already being searched for
        if(currentContext.httpRequestKeyword == currentContext.userKeyword)
        {
            // display the results array
            currentContext.displayResults(currentContext.httpRequestKeyword, nameArray);
        }
        else
        {
            // add the results to the cache
            // we don't need to display the results since they are no longer useful
            currentContext.addToCache(currentContext.httpRequestKeyword, nameArray);
        }
    }
}

/* populates the list with the current suggestions */
function displayResults(keyword, results_array)
{
    this.currentSuggestions = results_array;

    // if the searched for keyword is not in the cache then add it to the cache
    if(!this.oCache[keyword] && keyword)
        this.addToCache(keyword, results_array);
    // if the array of results is empty display a message
    if(results_array.length == 0)
    {

        this.hideSuggestions();

        // set the flag indicating that no results have been found
        // and reset the counter for results
        this.hasResults = false;
        this.suggestionCount = 0;
    }
    // display the results
    else
    {
        var div = "<ul>";
        // resets the index of the currently selected suggestion
        this.position = -1;
        // resets the flag indicating whether the up or down key has been pressed
        this.isKeyUpDownPressed = false;
        /* sets the flag indicating that there are results for the searched
       for keyword */
        this.hasResults = true;
        // get the number of results from the cache
        this.suggestionCount = this.oCache[keyword].length;
        // loop through all the results and generate the HTML list of rinitData[currentField].esults
        for (var i=0; i<this.oCache[keyword].length; i++)
        {
            // retrieve the current suggestion
            strSuggestion = this.oCache[keyword][i].label;

            div += "<li id='suggestion_"+this.oKeyword.id +"_" +i +
                "' onclick='selected(\""+this.oKeyword.id+"\", "+ i +")' onmouseover='handleOnMouseOver(this);' " +
                "onmouseout='handleOnMouseOut(this);'>" +
                "<a href='#nogo";
            // check to see if the current function name length exceeds the maximum
            // number of characters that can be displayed for a function name
            if(strSuggestion.length <= this.suggestionMaxLength)
            {
                // bold the matching prefix of the function name and of the keyword
                div += "'><b>" +
                    strSuggestion.substring(0, this.httpRequestKeyword.length) +
                    "</b>"
                div += strSuggestion.substring(this.httpRequestKeyword.length,
                strSuggestion.length) +
                    "</a></li>";
            }
            else
            {
                // check to see if the length of the current keyword exceeds
                // the maximum number of characters that can be displayed
                if(this.httpRequestKeyword.length < this.suggestionMaxLength)
                {
                    /* bold the matching prefix of the function name and that of the
             keyword */
                    div += "'><b>" +
                        strSuggestion.substring(0, this.httpRequestKeyword.length) +
                        "</b>"
                    div += strSuggestion.substring(this.httpRequestKeyword.length,
                    this.suggestionMaxLength) +
                        "</a></li>";
                }
                else
                {
                    // bold the entire function name
                    div += "'><b>" +
                        strSuggestion.substring(0,this.suggestionMaxLength) +
                        "</b></li>"
                }
            }
        }
        var oSuggest = document.getElementById(this.oKeyword.id+"_suggest");
        var oScroll = document.getElementById(this.oKeyword.id+"_scroll");
        // scroll to the top of the list
        oScroll.scrollTop = 0;
        div += "</ul>";
        // update the suggestions list and make it visible
        oSuggest.innerHTML = div;
        oScroll.style.display = "block";
        // if we had results we apply the type ahead for the current keyword
        if(results_array.length > 0)
            this.autocompleteKeyword();
    }

}

/* function that periodically checks to see if the typed keyword has changed */
function checkForChanges(keywordId)
{
    var currentContext = suggestData[keywordId];
    // retrieve the keyword object
    //    var oId = document.getElementById(idId);
    var keyword = currentContext.oKeyword.value;
    //    var originalText = document.getElementById("originalUserText");
    //    if (originalText!=null) {
    //        originalText.value = keyword.value;
    //    }

    // check to see if the keyword is empty
    if(keyword == "")
    {
        // hide the suggestions
        currentContext.hideSuggestions();
        // reset the keywords
        currentContext.userKeyword="";
        currentContext.httpRequestKeyword="";
        currentContext.oId.value = 0;
    }
    // set the timer for a new check
    setTimeout("checkForChanges('"+keywordId+"')", 500);
    // check to see if there are any changes
    if((currentContext.userKeyword != keyword) &&
        (currentContext.autocompletedKeyword != keyword) &&
        (!currentContext.isKeyUpDownPressed) &&
        keyword.length>1) {
        // update the suggestions
        currentContext.getSuggestions(keyword);
        currentContext.oId.value = 0
    }
}

/* function that handles the keys that are pressed */
function handleKeyUp(e)
{

    // get the event
    var ev = e || event;
    // get the event's target
    var target = ev.target || ev.srcElement;

    if (target.nodeType == 3)
        target = target.parentNode;
    var currentContext = suggestData[target.id];
    // get the character code of the pressed button
    code = (ev.charCode) ? ev.charCode :
        ((ev.keyCode) ? ev.keyCode :
        ((ev.which) ? ev.which : 0));
    // check to see if the event was keyup
    if (ev.type == "keyup")
    {
        currentContext.isKeyUpDownPressed =false;
        // check to see we if are interested in the current character
        if ((code < 13 && code != 8) ||
            (code >=14 && code < 32) ||
            (code >= 33 && code <= 46 && code != 38 && code != 40) ||
            (code >= 112 && code <= 123))
        {
            // simply ignore non-interesting characters
        }
        else
        /* if Enter is pressed we jump to the PHP help page of the current
       function */
            if(code == 13)
        {
            // check to see if any function is currently selected
            if(currentContext.position>=0)
            {
                selected(target.id, currentContext.position);
            }
        }
        else
        // if the down arrow is pressed we go to the next suggestion
            if(code == 40)
        {
            newTR=document.getElementById("suggestion_"+target.id+"_"+(++currentContext.position));
            oldTR=document.getElementById("suggestion_"+target.id+"_"+(--currentContext.position));
            // deselect the old selected suggestion
            if(currentContext.position>=0 && currentContext.position<currentContext.suggestionCount-1)
                oldTR.className = "";

            // select the new suggestion and update the keyword
            if(currentContext.position < currentContext.suggestionCount - 1)
            {
                newTR.className = "highlightrow";
                //                updateKeywordValue(newTR);
                currentContext.position++;
                currentContext.oKeyword.value = currentContext.currentSuggestions[currentContext.position].label
            }
            e.cancelBubble = true;
            e.returnValue = false;
            currentContext.isKeyUpDownPressed = true;
            // scroll down if the current window is no longer valid
            if(currentContext.position > currentContext.maxVisiblePosition)
            {
                oScroll = document.getElementById(target.id+"_scroll");
                oScroll.scrollTop += 18;
                currentContext.maxVisiblePosition += 1;
                currentContext.minVisiblePosition += 1;
            }
        }
        else
        // if the up arrow is pressed we go to the previous suggestion
            if(code == 38)
        {
            newTR=document.getElementById("suggestion_"+target.id+"_"+(--currentContext.position));
            oldTR=document.getElementById("suggestion_"+target.id+"_"+(++currentContext.position));
            // deselect the old selected position
            if(currentContext.position>=0 && currentContext.position <= currentContext.suggestionCount - 1)
            {
                oldTR.className = "";
            }
            // select the new suggestion and update the keyword
            if(currentContext.position > 0)
            {
                newTR.className = "highlightrow";
                //                updateKeywordValue(newTR);

                currentContext.position--;
                currentContext.oKeyword.value = currentContext.currentSuggestions[currentContext.position].label
                // scroll up if the current window is no longer valid
                if(currentContext.position<currentContext.minVisiblePosition)
                {
                    oScroll = document.getElementById(target.id+"_scroll");
                    oScroll.scrollTop -= 18;
                    currentContext.maxVisiblePosition -= 1;
                    currentContext.minVisiblePosition -= 1;
                }
            }
            else
                if(currentContext.position == 0)
                    currentContext.position--;
            ev.cancelBubble = true;
            ev.returnValue = false;
            currentContext.isKeyUpDownPressed = true;
        }
    }
}

/* function that removes the style from all suggestions*/
function deselectAll()
{
    for(i=0; i<this.suggestionCount; i++)
    {
        var oCrtTr = document.getElementById("suggestion_"+this.oKeyword.id +"_"+ i);
        oCrtTr.className = "";
    }
}

/* function that handles the mouse entering over a suggestion's area
   event */
function handleOnMouseOver(oTr)
{
    deselectAll();
    oTr.className = "highlightrow";
    position = oTr.id.substring(2, oTr.id.length);
}

/* function that handles the mouse exiting a suggestion's area event */
function handleOnMouseOut(oTr)
{
    oTr.className = "";
    position = -1;
}


/* function that hides the layer containing the suggestions */
function hideSuggestions()
{
    var oScroll = document.getElementById(this.oKeyword.id+"_scroll");
    oScroll.style.display = "none";
}

/* function that selects a range in the text object passed as parameter */
function selectRange(start, length)
{
    // check to see if in IE or FF
    if (this.oKeyword.createTextRange)
    {
        //IE
        var oRange = this.oKeyword.createTextRange();
        oRange.moveStart("character", start);
        oRange.moveEnd("character", length - this.oKeyword.value.length);
        oRange.select();

    }
    else
    // FF
        if (this.oKeyword.setSelectionRange)
    {
        this.oKeyword.setSelectionRange(start, length);
    }
    this.oKeyword.focus();
}

/* function that autocompletes the typed keyword*/
function autocompleteKeyword()
{
    // reset the position of the selected suggestion
    this.position=0;
    // deselect all suggestions
    this.deselectAll();
    // highlight the selected suggestion
    document.getElementById("suggestion_"+this.oKeyword.id+"_0").className="highlightrow";
    // update the keyword's value with the suggestion
    //    updateKeywordValue(document.getElementById("tr0"));
    this.oKeyword.value = this.currentSuggestions[0].label;
    // apply the type-ahead style
    this.selectRange(this.httpRequestKeyword.length, this.oKeyword.value.length);
    // set the autocompleted word to the keyword's value
    this.autocompletedKeyword = this.oKeyword.value;
}

function selected(keywordId, index) {
    //    console.log(occupationId);
    currentContext = suggestData[keywordId];
    
    currentContext.oKeyword.value = currentContext.currentSuggestions[index].label;
    currentContext.userKeyword = currentContext.oKeyword.value;
    currentContext.oId.value = currentContext.currentSuggestions[index].value;
    currentContext.hideSuggestions();
}
