/**
*    Json key/value autocomplete for jQuery
*    Provides a transparent way to have key/value autocomplete
*    Copyright (C) 2008 Ziadin Givan www.CodeAssembly.com
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU Lesser General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public License
*    along with this program.  If not, see http://www.gnu.org/licenses/
*
*    Examples
*    $("input#example").autocomplete("autocomplete.php");//using default parameters
*    $("input#example").autocomplete("autocomplete.php",{minChars:3,timeout:3000,validSelection:false,parameters:{'myparam':'myvalue'},before : function(input,text) {},after : function(input,text) {}});
*    minChars = Minimum characters the input must have for the ajax request to be made
*    timeOut = Number of miliseconds passed after user entered text to make the ajax request
*    validSelection = If set to true then will invalidate (set to empty) the value field if the text is not selected (or modified) from the list of items.
*    parameters = Custom parameters to be passed
*    after, before = a function that will be caled before/after the ajax request
*/

// preserve previous plugin, if existed
if (jQuery.fn.autocomplete) jQuery.fn.autocomplete_backup = jQuery.fn.autocomplete;

if (!!window.trans){
    trans = window.trans;
} else {
    trans = function(k) { return k; }
}

jQuery.fn.autocomplete = function(url, settings, default_value)
{
    //do it for each matched element
    return this.each(function () {
        //this is the original input
        var textInput = $(this);
        if (textInput.data('events')) {
            textInput.data('events').keydown = undefined;
        }
        var valueInput = $(this).next();
        //create the ul that will hold the text and values
        valueInput.after('<ul class="autocomplete" style="z-index: 100"></ul>');

        var pos = $(textInput).position();
        var oldText = '';
        var tt;
        var size = 0;
        var selected = 0;
        var alreadyAwaitingResult = false;
        var outData = null;
        var formIsLocked = false;
        var formField = $('#' + textInput.attr('id')).parents('form:first');
        var getDataInProgress = false;
        var dataInProgressCounter = 0;
        var pickedValue = valueInput.attr('name');
        var ajaxCall;
        var list = valueInput.next();
        var specRequest = valueInput.next();
        settings = jQuery.extend({
            minChars: 3,
            timeout: 250,
            after: null,
            before: null,
            callback: null, // called with a sys_path, after selecting a city
            validator: /.*/,
            validSelection: true,
            parameters: {
                'inputName': valueInput.attr('name'), 
                'inputId': textInput.attr('id')
            }
        }, settings);

        formField.bind('submit', function (e) {
            if (isGetDataInProgress()) {
                formField.trigger('enterWasHit');
                e.preventDefault();
                return false;
            }
        });

        function displayWarning() {
            list.html('<li id="lp">' + trans('The City/State/Zip code is not valid.') + '<br /> ' + trans('Please enter a valid city/state/zip code.') + '</li>');
        }

        function isFormLocked() {
            return formIsLocked === true ? true : false;
        }

        function isGetDataInProgress() {
            return getDataInProgress === true ? true : false;
        }

        var blockSubmitHandler = function (e) {
            e.stopPropagation();
            return false;
        };

        function putLastEventOnTop(event_type) {
            // this ressures that return false we have added aboce is put on the top of
            // event execution queue, just in case someone, somewhere is messing with our
            // object, when he shouldn't
            var handlers = formField.data('events')[event_type];
            var handler = handlers.splice(handlers.length - 1)[0];

            handlers.splice(0, 0, handler);
        }

        function lockSearch() {
            formIsLocked = true;

            formField.bind('submit', blockSubmitHandler);
            putLastEventOnTop('submit');
        }

        function unlockSearch() {
            formIsLocked = false;

            formField.unbind('submit', blockSubmitHandler);
        }

        function proceedWithSearch() {
            // form is unlocked when there is exactly one possible result
            if(!formIsLocked) {
                formField.trigger('submit');
            } else {
                //TODO: it was started never ended as customer did not need that feature
                //displayRequestForSpecifcation();
            }
        }

        formField.bind('getDataStarted', function () {
            getDataInProgress = true;
        });

        formField.bind('getDataEnded', function () {
            getDataInProgress = false;
        });

        formField.bind('enterWasHit', function () {
            if (isGetDataInProgress()) {
                // reassure that event is binded only once, one only reassures that it's executed only once
                if (!alreadyAwaitingResult) {
                    formField.one('getDataEnded', function () {
                        proceedWithSearch();
                        alreadyAwaitingResult = false;
                    });
                    alreadyAwaitingResult = true;
                }
            } else {
                proceedWithSearch();
            }
        });

        function getData(text) {
            list = valueInput.next().css({
                top: $(textInput).position().top + $(textInput).outerHeight() - 3, 
                left: $(textInput).position().left, 
                width: $(textInput).width() + 7
            });

            specRequest = valueInput.next().css({
                top: $(textInput).position().top + $(textInput).outerHeight() - 3, 
                left: $(textInput).position().left, 
                width: $(textInput).width() + 7
            });

            // this trigger must be outside getData to make sure that we trigger 
            // before settings.timeout which may delay execution for a lot of time
            formField.trigger('getDataStarted');

            $("#region_or_area_completer_touched").val("touched");

            if (!settings.validator.test(text) && text) {
                displayWarning();
                list.show();
                return;
            }
            if (text.length < 3) {
                clear();
            }
            if (settings.minChars !== null && text.length >= settings.minChars) {
                text = textInput.val();
                clear();
                if (settings.before) {
                    settings.before(textInput, text);
                }
                textInput.addClass('autocomplete-loading');
                settings.parameters.text = text;

                dataInProgressCounter += 1;
                exactMatch = false;

                if (typeof(ajaxCall) == 'object') {
                    if (ajaxCall.readyState != 4) {
                        if(!($.browser.msie && $.browser.version == '7.0')){
                            ajaxCall.abort();
                        }
                        ajaxCall = null;
                    }
                }

                ajaxCall = $.ajax({
                    url: url,
                    dataType: 'json',
                    data: settings.parameters,
                    success: function (data, textStatus, xhr) {
                        outData = data;
                        var items = '';
                        if(data !== null && data.err_happened != "true") {
                            if (data.completions.length > 0 || settings.callback) {
                                size = data.completions.length;
                                // iterate over all options
                                for (i = 0; i < size; i++) {
                                    var q = [];
                                    if (data.completions[i] instanceof Array) {
                                        var k = data.completions[i][1].split('');
                                        var tmp_sys_path = data.completions[i][0];
                                    } else {
                                        var k = data.completions[i].name.split('');
                                        var tmp_sys_path = data.completions[i].sys_path;
                                    }
                                    q[0] = "<strong>";
                                    for(var j=0; j<text.length; j++) {
                                        q[j+1]=k[j];
                                    }
                                    for(var w=text.length+1; w<k.length+2; w++) {
                                        if(w == text.length+1) {
                                            q[w] = "</strong>";
                                        } else {
                                            q[w] = k[w-2];
                                        }
                                    }
                                    var suggestionText = q.join("");
                                    items += '<li id="lp' + i + '" class="' + tmp_sys_path + '">' + suggestionText + '</li>';
                                }
                                list.html(items);
                                // on mouse hover over elements set selected class and on click set the selected value and close list
                                list.show();
                                hideSelectsForIE6(list);
                                list.children().mouseover(function () {
                                    $(this).addClass("selected");
                                });
                                list.children().mouseout(function () {
                                    $(this).removeClass("selected");
                                });
                                list.children().click(function () {
                                    var completion = data.completions[parseInt($(this).attr("id").replace("lp", ""))];
                                    var is_ambiguous = completion[2];
                                    var sys_path = completion[0];
                                    if (isFormLocked()) {
                                        unlockSearch();
                                    }
                                    textInput.val($(this).text());
                                    if (is_ambiguous === 'false') {
                                        valueInput.val(sys_path);
                                    } else {
                                        valueInput.val('');
                                    }
                                    clear();
                                    pickedValue = $(this).text();
                                    if (settings.callback) {
                                        if(completion.length != null){
                                            settings.callback(completion[0], completion);
                                        } else {
                                            settings.callback(completion.sys_path, completion);
                                        }
                                    }
                                });
                                if (data.completions.length == 1) {
                                    list.children().click();
                                } else if (data.completions.length == 0 && settings.callback) {
                                    displayWarning();
                                }
                                if (settings.after) {
                                    settings.after(textInput, text);
                                }
                            }
                            textInput.removeClass('autocomplete-loading');
                        } else if(data !== null) {
                            //alert(data.msg);
                        } else {
                            // this is commented as Request may be aborted on many 
                            // different ways, half of them is beyond our reach,
                            // ie clicking escape will issue this error
                            // alert('Request was aborted');
                        }
                    },
                    complete: function (XMLHttpRequest, textStatus) {
                        // when we have multiple getDataStarted we want to issue getDataEnded event 
                        // only once, its very important, because when press enter, the actuall enter
                        // is triggered when last getDataEnded has finished, it's done this way to 
                        // maintain the most precise result search, for example:
                        // a) user types "New" then wait 350ms
                        // b) user types " " then wait another 313ms
                        // c) then types "York, NY" and press enter
                        //
                        // Assuming that our timeout is set to 300ms there should 3x getData on the loose
                        // (and assuming each such getData taks 500ms+ to get response from server).
                        //
                        // User pressed enter so he want to prompt search, but he want to search for:
                        // "New York, NY" (in other words he wants to proceed with search, after last 
                        // getData has receveid it's response). Thus only last getData triggers 
                        // getDataEnded.
                        //
                        //
                        // Disclaimer.
                        //
                        // Thechnicall there is a possibility that there will be a gap in between of 
                        // execution of several getData or server will response to early. In effect 
                        // will trigger to early. There is no nown solution on that issue (yet).
                        if (dataInProgressCounter == 1) {
                            formField.trigger('getDataEnded');
                        }
                        if (dataInProgressCounter > 0) {
                            dataInProgressCounter -= 1;
                        }
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        //alert(textStatus);
                    }
                });

                oldText = text;
            }
        }

        function clear() {
            list.hide();
            size = 0;
            selected = -1;
            hideSelectsForIE6(list);
            pickedValue = '';
        }
        
        var idInter;
        var beforeText = '';
        var afterText = '';
        
        // this setting blocks form when we start typing in text field form
        // the lock is realeased when autocompleter has exactly one result
        if (settings.blockOnFocusReleaseOnResult) {
            textInput.bind('focus', lockSearch);
        }

        textInput.bind('keyup', function (e) {
            after = textInput.val();

            if(after <= settings.minChars) {
                window.clearInterval(tt);
                clear();
            }

            if ((e.which == 40 || e.which == 38) && before.length > 0) { // move up, down
                if (e.which == 40) {
                    selected = selected >= size - 1 ? 0 : selected + 1;
                } else if (e.which == 38) {
                    selected = selected <= 0 ? size - 1 : selected - 1;
                }
                // set selected item and input values
                if (outData.completions[parseInt(list.children().eq(selected).attr('id').replace("lp", ""))][2] === 'false') {
                    textInput.val(list.children().removeClass('selected').eq(selected).addClass('selected').text());
                    valueInput.val(outData.completions[parseInt(list.children().eq(selected).attr('id').replace("lp", ""))][0]);
                } else {
                    textInput.val(list.children().removeClass('selected').eq(selected).addClass('selected').text());
                    valueInput.val('');
                }
            } else if (after != before && after.length >= settings.minChars) {
                window.clearInterval(tt);

                if (settings.validSelection) {
                    valueInput.val('');
                }

                tt = window.setTimeout(function () { 
                    getData(after);
                }, settings.timeout);
            }
        });
        
        textInput.bind('keydown', function (e) {
            before = textInput.val();

            if (e.which == 13) {
                e.preventDefault();
                formField.trigger('enterWasHit');
            }
        });
    });
};

var _selectsList =[];
/*
 * function hide selects located under autocompletition list
 */
function hideSelectsForIE6(e){
    if($.browser.msie && $.browser.version>=6.0 && $.browser.version<7.0){
        var _listDimension = [];
        if($(e).css("display")!="none"){
            _listDimension.push($(e).offsetParent().position().left + $(e).position().left);
            _listDimension.push($(e).offsetParent().position().left + $(e).position().left + $(e).width());
            _listDimension.push($(e).offsetParent().position().top + $(e).position().top);
            _listDimension.push($(e).offsetParent().position().top + $(e).position().top + $(e).height());
            $("select").each(function(){
                if((parseInt(_listDimension[2],10) <= parseInt($(this).position().top,10) &&
                parseInt($(this).position().top,10)<= parseInt(_listDimension[3],10) &&
                parseInt(_listDimension[0],10) <= parseInt($(this).position().left,10) &&
                parseInt($(this).position().left,10) <= parseInt(_listDimension[1],10)) ||
                (parseInt(_listDimension[2],10) <= parseInt($(this).position().top,10)+$(this).height() &&
                parseInt($(this).position().top,10)+$(this).height()<= parseInt(_listDimension[3],10) &&
                parseInt(_listDimension[0],10) <= parseInt($(this).position().left,10)+$(this).width() &&
                parseInt($(this).position().left,10)+$(this).width() <= parseInt(_listDimension[1],10))){
                    _selectsList.push($(this).attr("id"));
                    $(this).css("visibility","hidden");
                }
            })
        }else{
            for(var i=0;i<_selectsList.length; i++){
                $("#"+_selectsList[i]).css("visibility","visible");
            }
        }
    }
}

function hideDefaultInputText(e){
    var oldText = null;
    var focusNo = null;
    $(e).focus(function(){
        if(focusNo==null){
            focusNo+=1;
            oldText = $(this).val();
            $(this).val("");
        }else{
            if($(this).val()==oldText){
                $(this).val("");
            }
        }
    })
    $(e).blur(function(){
        if($(this).val()==""){
            $(this).val(oldText);
        }
    })
}

