﻿(function ($) {

    $.fn.quickSearch = function (options) {

        var defaults = {
            lineSeparator: "\n",
            cellSeparator: "|",
            minChars: 3,
            delay: 200,
            matchSubset: 0,
            mustMatch: 0,
            extraParams: {},
            loadingClass: "SearchAutocompleteLoading",
            selectFirst: true,
            selectOnly: true,
            width: 0
        }; // Defaults for configurable properties are set in vb
        var config = $.extend(defaults, options);

        this.each(function () {

            var self = this;
            var $self = $(this);
            var $input = $("input", $self);
            var $results = $("#SearchResults", $self);

            // Private variables
            var timeout = null;
            var prev = "";
            var active = -1;
            var keyb = false;
            var hasFocus = false;
            var lastKeyPressCode = null;

            // Messages
            var msgExtraSearch = __('Search ~Target~ for <strong>~Query~</strong>')
            var msgNoResults = __('No pages found matching <strong>~Query~</strong>')

            hideResultsNow();

            // wire up keyboard shorcut (/ = focus search)
            $(document).bind('keydown', '/', function (e) {
                $input.focus();
                e.preventDefault();
            });

            // wire up keystrokes inside input
            $input
                .keydown(function (e) {
                    // track last key pressed
                    lastKeyPressCode = e.keyCode;
                    switch (e.keyCode) {
                        case 38: // up
                            e.preventDefault();
                            moveSelect(-1);
                            break;
                        case 40: // down
                            e.preventDefault();
                            moveSelect(1);
                            break;
                        case 9:  // tab
                        case 13: // return
                            if (selectCurrent()) {
                                // something was selected from the drop-down
                                $input.get(0).blur();
                                e.preventDefault();
                            } else {
                                // nothing was selected from the drop-down - forward this query to the search page
                                if (config.searchPage) {
                                    $r.showProgress();
                                    var searchUrl = options.searchPage + $.query.empty().set('s', $input.val());
                                    window.location = searchUrl;
                                    e.preventDefault();
                                }
                                if (config.ignoreEnter)
                                    e.preventDefault();
                            }
                            break;
                        default:
                            active = -1;
                            if (timeout) clearTimeout(timeout);
                            timeout = setTimeout(function () { onChange(); }, config.delay);
                            break;
                    }
                })
                .focus(function () {
                    // track whether the field has focus, we shouldn't process any results if the field no longer has focus
                    hasFocus = true;
                    this.select();
                    if (!!$input.val() && $input.val().length >= config.minChars && $input.val() != config.watermark && $("li", $results).length > 0) {
                        showResults();
                    };
                })
                .blur(function () {
                    // track whether the field has focus
                    hasFocus = false;
                    hideResults();
                });

            function onChange() {
                // ignore if the following keys are pressed: [del] [shift] [capslock]
                if (lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32)) {
                    hideResults();
                    return null;
                }
                var v = $input.val();
                if (v == prev) return;
                prev = v;
                if (v.length >= config.minChars) {
                    $input.addClass(config.loadingClass);
                    setTimeout(function () { requestData(v); }, 1);
                } else {
                    $input.removeClass(config.loadingClass);
                    hideResults();
                }
            };

            function moveSelect(step) {
                var lis = $("li", $results);
                if (!lis) return;
                active += step;
                if (active < 0) {
                    active = 0;
                } else if (active >= lis.size()) {
                    active = lis.size() - 1;
                }
                lis.removeClass("SearchAutocompleteOver");
                $(lis[active]).addClass("SearchAutocompleteOver");
            };

            function selectCurrent() {
                var li = $("li.SearchAutocompleteOver", $results)[0];
                if (!li) {
                    var $li = $("li", $results);
                    if (config.selectOnly) {
                        if ($li.length == 1) li = $li[0];
                    } else if (config.selectFirst) {
                        li = $li[0];
                    }
                }
                if (li) {
                    selectItem(li);
                    return true;
                } else {
                    return false;
                }
            };

            function selectItem(li) {
                if (!li) {
                    li = document.createElement("li");
                    li.selectValue = "";
                }
                var v = $.trim(li.selectValue);
                $input.lastSelected = v;
                prev = v;
                $results.html("");
                //hideResultsNow();
                if (v.indexOf(config.searchPage) >= 0 && config.isSearchPage) {
                    // we're already on the search page - do nothing (results are provided by the search page, e.g. Google custom search)
                } else if (!v || v == "") {
                    // no search string - do nothing
                } else {
                    // navigate to the URL of the selected item
                    $r.showProgress();
                    window.location = v;
                };
            };

            function showResults() {
                $self.addClass("Active");
            };

            function hideResults() {
                if (timeout) clearTimeout(timeout);
                timeout = setTimeout(hideResultsNow, 200);
            };

            function hideResultsNow() {
                if (timeout) clearTimeout(timeout);
                $self.removeClass("Active");
                if (config.mustMatch) {
                    var v = $input.val();
                    if (v != $input.lastSelected) {
                        selectItem(null);
                    }
                }
            };

            function receiveData(q, data) {
                if (data) {
                    $results.html("");
                    if (!hasFocus) return hideResultsNow();
                    $results.html(dataToDom(q, data));
                    $input.removeClass(config.loadingClass);
                    showResults();
                } else {
                    hideResultsNow();
                }
            };

            function dataToDom(q, data) {
                var $ul = $(document.createElement("ul"));
                // Add search link as first item
                if (config.searchPage) {
                    // Add "Search" heading
                    var $h = $(document.createElement("h4"));
                    $h.html("Search")
                        .appendTo($ul);
                    var $li = $(document.createElement("li"));
                    $li[0].selectValue = options.searchPage + '?s=' + q;
                    var siteSearch = msgExtraSearch.replace('~Target~', config.siteName).replace('~Query~', q);
                    $li.addClass("SearchAutocompleteOver") // first item highlighted at first
                        .addClass("GoRight")
                        .html(siteSearch)
                        .appendTo($ul);
                }
                // Add extra search pages (e.g. "Search contacts for 'Fred'"). 
                // The extraSearchPages option comes in the form "label:path,label:path,label.path" .
                // For example "Documents:/en/icma/knowledge_network/documents,People:/en/icma/knowledge_network/people"
                if (!!config.extraSearchPages) {
                    var searchDefs = config.extraSearchPages.split(",");
                    for (var i = 0; i < searchDefs.length; i++) {
                        var label = searchDefs[i].split(":")[0];
                        var path = searchDefs[i].split(":")[1];
                        var $li = $(document.createElement("li"));
                        $li[0].selectValue = String.format(path, q);
                        var extraSearch = msgExtraSearch.replace('~Target~', label).replace('~Query~', q);
                        $li.addClass("GoRight")
                            .html(extraSearch)
                            .appendTo($ul);
                    }
                }
                if (data.length == 0 && !config.searchPage) {
                    var $li = $(document.createElement("li"));
                    $li[0].selectValue = "/";
                    var noResults = msgNoResults.replace("~Query~", q);
                    $li.addClass("X")
                        .html(noResults)
                        .appendTo($ul);
                }
                // Limit results to a max number
                var maxItems = data.length;
                if ((config.maxItemsToShow > 0) && (config.maxItemsToShow < maxItems)) maxItems = config.maxItemsToShow;
                var previousHeading = "";
                // Add each data item as a list item
                var countThisType = 0;
                var countDisplayed = 0;
                for (var i = 0; i < data.length && countDisplayed < maxItems; i++) {
                    var row = data[i];
                    if (!row) continue;
                    var heading = row["ItemNamePlural"];
                    if (heading != previousHeading) {
                        // New ObjectName - make a heading (assumes results come sorted by ObjectName) 
                        var $listHeading = $(document.createElement("h4"));
                        $listHeading.html("<span>" + heading + "</span>")
                            .appendTo($ul); // not valid for a ul to contain an h4, but works
                        previousHeading = heading;
                        countThisType = 0
                    }
                    // Only show a few items of each type (maxItemsPerType)
                    if (countThisType < config.maxItemsPerType) {
                        // Make list item
                        $li = $(document.createElement("li"));
                        // highlight the search term in the results (case insenstive match)
                        $li[0].selectValue = row["Url"];
                        $li.html(row["Title"].replace(new RegExp("(" + $().escapeRegex(q) + ")", "i"), "<strong>$1</strong>"))
                            .attr("title", row["Url"])
                            .addClass(row["IconClass"])
                            .appendTo($ul);
                        countDisplayed++;
                        countThisType++;
                    }
                }
                // Wire up hover and click behaviors on list items
                $("li", $ul).
                    hover(
                        function () {
                            // mouseover
                            $("li", $ul).removeClass("SearchAutocompleteOver");
                            $(this).addClass("SearchAutocompleteOver");
                            active = $("li", $ul).indexOf($(this).get(0));
                        },
                        function () {
                            // mouseout
                            $(this).removeClass("SearchAutocompleteOver");
                        }).
                    click(
                        function (e) {
                            // click
                            e.preventDefault();
                            e.stopPropagation();
                            selectItem(this);
                        });
                return $ul;
            };


            // Get matches from server
            function requestData(q) {
                if ((typeof config.url == "string") && (config.url.length > 0)) {
                    var parameters = {
                        "SearchString": q,
                        "ContentObjects": config.contentObjects
                    };
                    $.ajax({
                        url: config.url,
                        data: JSON.stringify(parameters),
                        async: true, // changed from false to true to eliminate blocking in FireFox. See success below for implementation. 
                        type: "POST",
                        dataType: "json",
                        contentType: "application/json",
                        success: function (msg) {
                            data = msg.d;
                            // Only display these results if they match what's in the input
                            if ($input.val() == data.SearchString) {
                                receiveData(data.SearchString, data.Results);
                            }
                        },
                        error: function (XMLHttpRequest, textStatus, errorThrown) {
                            $r.showError(XMLHttpRequest, errorThrown);
                        }
                    });
                } else {
                    // no data found, remove the loading class
                    $input.removeClass(config.loadingClass);
                }
            };


        }); // each

    }; // $.fn.quickSearch 

})(jQuery);
