function stringMatch(str, query, options, special) {
        var result;

        options = options || {};

        // No query? Short circuit the normal work done and just
        // return a single range that covers the whole string.
        if (!query) {
            result = new SearchResult(str);
            result.matchGoodness = 0;
            if (DEBUG_SCORES) {
                result.scoreDebug = {};
            }
            result.stringRanges = [{
                text: str,
                matched: false,
                includesLastSegment: true
            }];
            return result;
        }

        // comparisons are case insensitive, so switch to lower case here
        var queryLower = query.toLowerCase();
        var compareLower = str.toLowerCase();

        if (options.preferPrefixMatches) {
            options.segmentedSearch = false;
        }

        if (options.preferPrefixMatches && compareLower.substr(0, queryLower.length) === queryLower) {
            // NOTE: we compare against the case insensitive match
            //        above but we pass the case-sensitive version in
            //        because we want to weight the match to give case-matches
            //        a higher score
            return _prefixMatchResult(str, query);
        }

        // Locate the special characters and then use orderedCompare to do the real
        // work.
        if (!special) {
            special = findSpecialCharacters(str);
        }
        var lastSegmentStart, matchList;

        // For strings that are not broken into multiple segments, we can potentially
        // avoid some extra work
        if (options.segmentedSearch) {
            lastSegmentStart = special.specials[special.lastSegmentSpecialsIndex];
            matchList = _wholeStringSearch(queryLower, compareLower, query, str, special.specials,
                              special.lastSegmentSpecialsIndex);
        } else {
            lastSegmentStart = 0;
            matchList = _generateMatchList(queryLower, compareLower, query, str, special.specials, 0);
        }

        // If we get a match, turn this into a SearchResult as expected by the consumers
        // of this API.
        if (matchList) {
            var compareData = _computeRangesAndScore(matchList, str, lastSegmentStart);
            result = new SearchResult(str);
            result.stringRanges = compareData.ranges;
            result.matchGoodness = -1 * compareData.matchGoodness;
            if (DEBUG_SCORES) {
                result.scoreDebug = compareData.scoreDebug;
            }
        }
        return result;
    }
function stringMatch(str, query, options, special) {
        var result;

        options = options || {};

        // No query? Short circuit the normal work done and just
        // return a single range that covers the whole string.
        if (!query) {
            result = new SearchResult(str);
            result.matchGoodness = 0;
            if (DEBUG_SCORES) {
                result.scoreDebug = {};
            }
            result.stringRanges = [{
                text: str,
                matched: false,
                includesLastSegment: true
            }];
            return result;
        }

        // comparisons are case insensitive, so switch to lower case here
        var queryLower = query.toLowerCase();
        var compareLower = str.toLowerCase();

        if (options.preferPrefixMatches) {
            options.segmentedSearch = false;
        }

        if (options.preferPrefixMatches && compareLower.substr(0, queryLower.length) === queryLower) {
            // NOTE: we compare against the case insensitive match
            //        above but we pass the case-sensitive version in
            //        because we want to weight the match to give case-matches
            //        a higher score
            return _prefixMatchResult(str, query);
        }

        // Locate the special characters and then use orderedCompare to do the real
        // work.
        if (!special) {
            special = findSpecialCharacters(str);
        }
        var lastSegmentStart, matchList;

        // For strings that are not broken into multiple segments, we can potentially
        // avoid some extra work
        if (options.segmentedSearch) {
            lastSegmentStart = special.specials[special.lastSegmentSpecialsIndex];
            matchList = _wholeStringSearch(queryLower, compareLower, query, str, special.specials,
                              special.lastSegmentSpecialsIndex);
        } else {
            lastSegmentStart = 0;
            matchList = _generateMatchList(queryLower, compareLower, query, str, special.specials, 0);
        }

        // If we get a match, turn this into a SearchResult as expected by the consumers
        // of this API.
        if (matchList) {
            var compareData = _computeRangesAndScore(matchList, str, lastSegmentStart);
            result = new SearchResult(str);
            result.stringRanges = compareData.ranges;
            result.matchGoodness = -1 * compareData.matchGoodness;
            if (DEBUG_SCORES) {
                result.scoreDebug = compareData.scoreDebug;
            }
        }
        return result;
    }
function stringMatch ( str , query , options , special ) { var result ; options = options || { } ; // No query? Short circuit the normal work done and just // return a single range that covers the whole string. if ( ! query ) { result = new SearchResult ( str ) ; result . matchGoodness = 0 ; if ( DEBUG_SCORES ) { result . scoreDebug = { } ; } result . stringRanges = [ { text : str , matched : false , includesLastSegment : true } ] ; return result ; } // comparisons are case insensitive, so switch to lower case here var queryLower = query . toLowerCase ( ) ; var compareLower = str . toLowerCase ( ) ; if ( options . preferPrefixMatches ) { options . segmentedSearch = false ; } if ( options . preferPrefixMatches && compareLower . substr ( 0 , queryLower . length ) === queryLower ) { // NOTE: we compare against the case insensitive match //        above but we pass the case-sensitive version in //        because we want to weight the match to give case-matches //        a higher score return _prefixMatchResult ( str , query ) ; } // Locate the special characters and then use orderedCompare to do the real // work. if ( ! special ) { special = findSpecialCharacters ( str ) ; } var lastSegmentStart , matchList ; // For strings that are not broken into multiple segments, we can potentially // avoid some extra work if ( options . segmentedSearch ) { lastSegmentStart = special . specials [ special . lastSegmentSpecialsIndex ] ; matchList = _wholeStringSearch ( queryLower , compareLower , query , str , special . specials , special . lastSegmentSpecialsIndex ) ; } else { lastSegmentStart = 0 ; matchList = _generateMatchList ( queryLower , compareLower , query , str , special . specials , 0 ) ; } // If we get a match, turn this into a SearchResult as expected by the consumers // of this API. if ( matchList ) { var compareData = _computeRangesAndScore ( matchList , str , lastSegmentStart ) ; result = new SearchResult ( str ) ; result . stringRanges = compareData . ranges ; result . matchGoodness = - 1 * compareData . matchGoodness ; if ( DEBUG_SCORES ) { result . scoreDebug = compareData . scoreDebug ; } } return result ; }
/*
Match str against the query using the QuickOpen algorithm provided by
the functions above. The general idea is to prefer matches of "special" characters and,
optionally, matches that occur in the "last segment" (generally, the filename). stringMatch
will try to provide the best match and produces a "matchGoodness" score
to allow for relative ranking.

The result object returned includes "stringRanges" which can be used to highlight
the matched portions of the string, in addition to the "matchGoodness"
mentioned above. If DEBUG_SCORES is true, scoreDebug is set on the result
to provide insight into the score.

The matching is done in a case-insensitive manner.

@param {string} str  The string to search
@param {string} query  The query string to find in string
@param {{preferPrefixMatches:?boolean, segmentedSearch:?boolean}} options to control search behavior.
preferPrefixMatches puts an exact case-insensitive prefix match ahead of all other matches,
even short-circuiting the match logic. This option implies segmentedSearch=false.
When segmentedSearch is true, the string is broken into segments by "/" characters
and the last segment is searched first and matches there are scored higher.
@param {?Object} special (optional) the specials data from findSpecialCharacters, if already known
This is generally just used by StringMatcher for optimization.
@return {{ranges:Array.<{text:string, matched:boolean, includesLastSegment:boolean}>, matchGoodness:int, scoreDebug: Object}} matched ranges and score
/ * Match str against the query using the QuickOpen algorithm provided by the functions above . The general idea is to prefer matches of special characters and optionally matches that occur in the last segment ( generally the filename ) . stringMatch will try to provide the best match and produces a matchGoodness score to allow for relative ranking .
Match a string against a query