﻿// On document ready...
$(function () {
    // URLs to hit.
    // all requests go through a load balancer
    var baseUrl = "http://auto.search.beyond.com:84";
    var locationFullSearchUrl = baseUrl + "/location/";
    var locationPartialSearchUrl = baseUrl + "/citystate/";
    var jobSearchUrl = baseUrl + "/keywords/";
    var jobTitleUrl = baseUrl + "/keywords/";                       //brettl
    var jobTitleInputClass = ".autocomplete_jobtitlecombo";
    var keywordInputClass = ".autocomplete_jobsearchcombo";
    var keywordInputClassNoFocus = ".autocomplete_jobsearchcombo_no_focus";
    var locationInputClass = ".autocomplete_location";
    var ajaxRequestSequenceNumber = 0;

    // Set up autocomplete for the job, location, and job title inputs, focusing the job input, turning off autocomplete
    initializeAutocomplete($(jobTitleInputClass).attr("autocomplete", "off"));           //brettl
    initializeAutocomplete($(keywordInputClass).focus().attr("autocomplete", "off"));
    initializeAutocomplete($(keywordInputClassNoFocus).attr("autocomplete", "off"));
    initializeAutocomplete($(locationInputClass).attr("autocomplete", "off"));


    function initializeAutocomplete(textInputSelection) {
        textInputSelection.autocomplete({
            source: function (request, response) {
                var performingLocationSearch = textInputSelection.is(locationInputClass);
                var url = performingLocationSearch ? locationPartialSearchUrl : jobSearchUrl;
                var originalTerm = request.term;
                var term = originalTerm;


                // Normalize and wildcard the input term
                term = wildcardTerm(normalizeTerm(term));

                // For debugging, display the wildcarded term on-page
                $("#term").text(term);

                // URL parameters
                var data = {
                    // async call request identifier
                    ajaxRequestSequenceNumber: ++ajaxRequestSequenceNumber,

                    // Transport format
                    wt: "json",

                    // Makes returned JSON pretty
                    indent: "on",

                    // Number of results		
                    rows: 10,

                    // Query
                    q: term,

                    // Facet queries (if any)
                    fq: [],

                    // Highlighting parameters
                    hl: "true",
                    "hl.fl": "text",
                    "hl.highlightMultiTerm": "true",
                    "hl.snippets": 100,
                    "hl.simple.pre": "<strong>",
                    "hl.simple.post": "</strong>",
                    "hl.requireFieldMatch": "true"
                };

                if (performingLocationSearch) {
                    // For location searches,	if any numbers are present,
                    // use the full search that includes zip.				
                    // Otherwise 
                    if ($(locationInputClass).val().match(/\d+/) != undefined)
                        url = locationFullSearchUrl;

                    // Tack on facet queries if any are specified in the dropdowns
                    if (regionCode != "")
                        data.fq.push("regioncodelist:" + regionCode);
                    if (countryCode != "")
                        data.fq.push("countrycode:" + countryCode);
                    if (cbsaCode != "")
                        data.fq.push("cbsacodelist:" + cbsaCode);

                    // Sort results by weight
                    data.sort = "weight desc";
                } else {
                    // For job searches, filter on primary industry ID
                    // and sort results by job search score
                    data.fq.push("primaryindustryid:" + industryId);
                    data.sort = textInputSelection.is(jobTitleInputClass) ? "jobtitlescore desc" : "jobsearchscore desc";
                }

                $.ajax({
                    url: url,

                    // Tell jQuery that we want to use JSONP as our
                    // transport mechanism
                    dataType: "jsonp",
                    jsonp: "json.wrf",

                    // jQuery 1.4 "helpfully" serializes parameter arrays to make 
                    // them easy for PHP and Ruby to consume.  We don't want that
                    traditional: true,

                    // URL parameters
                    data: data,

                    // Invoked by jQuery when the Ajax call has finished
                    success: function (data) {
                        // If this is not a response for the most-recently performed Ajax call, ignore it
                        if (parseInt(data.responseHeader.params.ajaxRequestSequenceNumber) < ajaxRequestSequenceNumber)
                            return;

                        // The response() function provides data to the autocompleter -
                        // the mapping function parameter it takes returns an array of
                        // label (the highlighted text shown in the dropdown) and value
                        // (unhighlighted raw text displayed on selection) pairs. 
                        response($.map(data.response.docs, function (doc) {
                            var realText = performingLocationSearch ? doc.locationstr[0] : doc.keyword[0];
                            var highlightedText = performingLocationSearch
									? highlight(realText, data.highlighting[doc.filekey[0]].text.join(" "))
									: data.highlighting[doc.filekey[0]].text[0];

                            return {
                                label: highlightedText,
                                value: realText
                            }
                        }));
                    }
                })
            },

            // Wait 100 ms after last keystroke to begin searching
            delay: 100,

            // Wait until there are at least 2 characters before hitting the server
            minLength: 2,

            // Custom attribute added by Xmog to jQuery UI to cap maximum length of autocompleter.
            // Needed this because there is an autocompleter bug with Chrome that will expand
            // the dropdown to several thousand pixels
            maxWidth: textInputSelection.width() + 50,

            // The autocompleter will invoke this after a match is selected by the user.
            // If we just autocompleted on job, we switch input focus to location automatically
            select: function (event, ui) {
                if (textInputSelection.is(keywordInputClass))
                    setTimeout(function () {
                        $(locationInputClass).focus();
                    }, 10);
            }
        });
    }

    // Given a search term: lowercase it, remove illegal characters entirely,
    // escape Lucene-reserved characters, and return the result.
    function normalizeTerm(term) {
        // Special handling for apostrophes - change "'s" to "s",
        // strip all other apostrophes entirely
        term = replace(term, "'s", "s");
        term = replace(term, "'", "");

        // Remove ignored characters
        $.each(",`-".split(""), function (i, ignoredCharacter) {
            term = replace(term, ignoredCharacter, " ");
        });

        // Escape Lucene-reserved characters, except "-" which is handled above
        $.each("\\+&|!(){}[]^\"~*?:".split(""), function (i, luceneCharacter) {
            term = replace(term, luceneCharacter, "\\" + luceneCharacter);
        });

        return term.toLowerCase();
    }

    // Replaces all instances of oldValue in string with newValue, ignoring case,
    // and returns the result.
    function replace(string, oldValue, newValue) {
        return string.replace(new RegExp(escapeRegex(oldValue), "gi"), newValue);
    }

    // Escapes all special regex characters in string and returns the result.
    // Useful for using a user-entered string as a regex.
    // Thanks to Theodor Zoulias		
    function escapeRegex(string) {
        return string.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
    }

    // Given a search term, add Lucene wildcard characters to
    // the end of each non-whitespace token and return the result.
    function wildcardTerm(term) {
        var wildcardTerm = "";
        $.each(term.split(" "), function (i, termChunk) {
            termChunk = $.trim(termChunk);
            if (termChunk != "")
                wildcardTerm += termChunk + "* ";
        });

        return wildcardTerm;
    }

    // Returns all indexes of the given value in the given string, or
    // an empty array if no indexes are present
    function indexesOf(value, string) {
        var indexes = [];
        var startIndex = string.indexOf(value);

        while (startIndex != -1) {
            indexes.push(startIndex);
            startIndex = string.indexOf(value, startIndex + 1);
        }

        return indexes;
    }

    // Given raw, unhighlighted text and the Solr highlighted text chunks,
    // replace the raw text's instances of the Solr highlighted chunks and return the result.
    // This is because Solr does not necessarily return the entire display string if only certain
    // chunks are highlighted
    function highlight(realText, solrHighlightedText) {
        var highlightedText = realText;
        var highlightRegex = new RegExp("<strong>.*<\/strong>", "gi");
        var regexMatches = highlightRegex.exec(solrHighlightedText);

        if (regexMatches) {
            var highlightingTagStartIndexes = indexesOf("<strong>", solrHighlightedText);
            var highlightingTagEndIndexes = indexesOf("</strong>", solrHighlightedText);

            var highlightedChunks = [];

            for (var i = 0; i < highlightingTagStartIndexes.length; i++)
                highlightedChunks.push(solrHighlightedText.substring(highlightingTagStartIndexes[i] + "<strong>".length, highlightingTagEndIndexes[i]));

            // Sort the chunks by length descending so we highlight longest first.
            highlightedChunks.sort(function (first, second) {
                return second.length - first.length;
            });

            for (var i = 0; i < highlightedChunks.length; i++) {
                var matches = highlightedText.match(new RegExp(escapeRegex(highlightedChunks[i]), "gi"));

                if (matches)
                    for (var j = 0; j < matches.length; j++)
                        highlightedText = replace(highlightedText, matches[j], "<strong>" + matches[j] + "</strong>");
            }
        }

        return highlightedText;
    }
});	

