src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js
changeset 54619 b43cc3b9ef40
parent 54589 3babb3ed24c6
equal deleted inserted replaced
54618:152c6c501ba5 54619:b43cc3b9ef40
     1 /*
     1 /*
     2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    28 var catPackages = "Packages";
    28 var catPackages = "Packages";
    29 var catTypes = "Types";
    29 var catTypes = "Types";
    30 var catMembers = "Members";
    30 var catMembers = "Members";
    31 var catSearchTags = "SearchTags";
    31 var catSearchTags = "SearchTags";
    32 var highlight = "<span class=\"resultHighlight\">$&</span>";
    32 var highlight = "<span class=\"resultHighlight\">$&</span>";
    33 var camelCaseRegexp = "";
    33 var searchPattern = "";
    34 var secondaryMatcher = "";
    34 var RANKING_THRESHOLD = 2;
       
    35 var NO_MATCH = 0xffff;
       
    36 var MAX_RESULTS_PER_CATEGORY = 500;
    35 function escapeHtml(str) {
    37 function escapeHtml(str) {
    36     return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
    38     return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
    37 }
    39 }
    38 function getHighlightedText(item) {
    40 function getHighlightedText(item, matcher) {
    39     var ccMatcher = new RegExp(escapeHtml(camelCaseRegexp));
       
    40     var escapedItem = escapeHtml(item);
    41     var escapedItem = escapeHtml(item);
    41     var label = escapedItem.replace(ccMatcher, highlight);
    42     return escapedItem.replace(matcher, highlight);
    42     if (label === escapedItem) {
       
    43         var secMatcher = new RegExp(escapeHtml(secondaryMatcher.source), "i");
       
    44         label = escapedItem.replace(secMatcher, highlight);
       
    45     }
       
    46     return label;
       
    47 }
    43 }
    48 function getURLPrefix(ui) {
    44 function getURLPrefix(ui) {
    49     var urlPrefix="";
    45     var urlPrefix="";
    50     var slash = "/";
    46     var slash = "/";
    51     if (ui.item.category === catModules) {
    47     if (ui.item.category === catModules) {
    62     } else {
    58     } else {
    63         return urlPrefix;
    59         return urlPrefix;
    64     }
    60     }
    65     return urlPrefix;
    61     return urlPrefix;
    66 }
    62 }
       
    63 function makeCamelCaseRegex(term) {
       
    64     var pattern = "";
       
    65     var isWordToken = false;
       
    66     term.replace(/,\s*/g, ", ").trim().split(/\s+/).forEach(function(w, index) {
       
    67         if (index > 0) {
       
    68             // whitespace between identifiers is significant
       
    69             pattern += (isWordToken && /^\w/.test(w)) ? "\\s+" : "\\s*";
       
    70         }
       
    71         var tokens = w.split(/(?=[A-Z,.()<>[\/])/);
       
    72         for (var i = 0; i < tokens.length; i++) {
       
    73             var s = tokens[i];
       
    74             if (s === "") {
       
    75                 continue;
       
    76             }
       
    77             pattern += $.ui.autocomplete.escapeRegex(s);
       
    78             isWordToken =  /\w$/.test(s);
       
    79             if (isWordToken) {
       
    80                 pattern += "([a-z0-9_$<>\\[\\]]*?)";
       
    81             }
       
    82         }
       
    83     });
       
    84     return pattern;
       
    85 }
       
    86 function createMatcher(pattern, flags) {
       
    87     var isCamelCase = /[A-Z]/.test(pattern);
       
    88     return new RegExp(pattern, flags + (isCamelCase ? "" : "i"));
       
    89 }
    67 var watermark = 'Search';
    90 var watermark = 'Search';
    68 $(function() {
    91 $(function() {
    69     $("#search").val('');
    92     $("#search").val('');
    70     $("#search").prop("disabled", false);
    93     $("#search").prop("disabled", false);
    71     $("#reset").prop("disabled", false);
    94     $("#reset").prop("disabled", false);
    91     _create: function() {
   114     _create: function() {
    92         this._super();
   115         this._super();
    93         this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
   116         this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
    94     },
   117     },
    95     _renderMenu: function(ul, items) {
   118     _renderMenu: function(ul, items) {
    96         var rMenu = this,
   119         var rMenu = this;
    97                 currentCategory = "";
   120         var currentCategory = "";
    98         rMenu.menu.bindings = $();
   121         rMenu.menu.bindings = $();
    99         $.each(items, function(index, item) {
   122         $.each(items, function(index, item) {
   100             var li;
   123             var li;
   101             if (item.l !== noResult.l && item.category !== currentCategory) {
   124             if (item.l !== noResult.l && item.category !== currentCategory) {
   102                 ul.append("<li class=\"ui-autocomplete-category\">" + item.category + "</li>");
   125                 ul.append("<li class=\"ui-autocomplete-category\">" + item.category + "</li>");
   112             }
   135             }
   113         });
   136         });
   114     },
   137     },
   115     _renderItem: function(ul, item) {
   138     _renderItem: function(ul, item) {
   116         var label = "";
   139         var label = "";
       
   140         var matcher = createMatcher(escapeHtml(searchPattern), "g");
   117         if (item.category === catModules) {
   141         if (item.category === catModules) {
   118             label = getHighlightedText(item.l);
   142             label = getHighlightedText(item.l, matcher);
   119         } else if (item.category === catPackages) {
   143         } else if (item.category === catPackages) {
   120             label = (item.m)
   144             label = (item.m)
   121                     ? getHighlightedText(item.m + "/" + item.l)
   145                     ? getHighlightedText(item.m + "/" + item.l, matcher)
   122                     : getHighlightedText(item.l);
   146                     : getHighlightedText(item.l, matcher);
   123         } else if (item.category === catTypes) {
   147         } else if (item.category === catTypes) {
   124             label = (item.p)
   148             label = (item.p)
   125                     ? getHighlightedText(item.p + "." + item.l)
   149                     ? getHighlightedText(item.p + "." + item.l, matcher)
   126                     : getHighlightedText(item.l);
   150                     : getHighlightedText(item.l, matcher);
   127         } else if (item.category === catMembers) {
   151         } else if (item.category === catMembers) {
   128             label = getHighlightedText(item.p + "." + (item.c + "." + item.l));
   152             label = getHighlightedText(item.p + "." + (item.c + "." + item.l), matcher);
   129         } else if (item.category === catSearchTags) {
   153         } else if (item.category === catSearchTags) {
   130             label = getHighlightedText(item.l);
   154             label = getHighlightedText(item.l, matcher);
   131         } else {
   155         } else {
   132             label = item.l;
   156             label = item.l;
   133         }
   157         }
   134         var li = $("<li/>").appendTo(ul);
   158         var li = $("<li/>").appendTo(ul);
   135         var div = $("<div/>").appendTo(li);
   159         var div = $("<div/>").appendTo(li);
   144             div.html(label);
   168             div.html(label);
   145         }
   169         }
   146         return li;
   170         return li;
   147     }
   171     }
   148 });
   172 });
       
   173 function rankMatch(match, category) {
       
   174     if (!match) {
       
   175         return NO_MATCH;
       
   176     }
       
   177     var index = match.index;
       
   178     var input = match.input;
       
   179     var leftBoundaryMatch = 2;
       
   180     var periferalMatch = 0;
       
   181     var delta = 0;
       
   182     // make sure match is anchored on a left word boundary
       
   183     if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index - 1] || "_" === input[index]) {
       
   184         leftBoundaryMatch = 0;
       
   185     } else if (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input)) {
       
   186         leftBoundaryMatch = 1;
       
   187     }
       
   188     var matchEnd = index + match[0].length;
       
   189     var leftParen = input.indexOf("(");
       
   190     // exclude peripheral matches
       
   191     if (category !== catModules && category !== catSearchTags) {
       
   192         var endOfName = leftParen > -1 ? leftParen : input.length;
       
   193         var delim = category === catPackages ? "/" : ".";
       
   194         if (leftParen > -1 && leftParen < index) {
       
   195             periferalMatch += 2;
       
   196         } else if (input.lastIndexOf(delim, endOfName) >= matchEnd) {
       
   197             periferalMatch += 2;
       
   198         }
       
   199     }
       
   200     for (var i = 1; i < match.length; i++) {
       
   201         // lower ranking if parts of the name are missing
       
   202         if (match[i])
       
   203             delta += match[i].length;
       
   204     }
       
   205     if (category === catTypes) {
       
   206         // lower ranking if a type name contains unmatched camel-case parts
       
   207         if (/[A-Z]/.test(input.substring(matchEnd)))
       
   208             delta += 5;
       
   209         if (/[A-Z]/.test(input.substring(0, index)))
       
   210             delta += 5;
       
   211     }
       
   212     return leftBoundaryMatch + periferalMatch + (delta / 200);
       
   213 
       
   214 }
   149 $(function() {
   215 $(function() {
   150     $("#search").catcomplete({
   216     $("#search").catcomplete({
   151         minLength: 1,
   217         minLength: 1,
   152         delay: 300,
   218         delay: 300,
   153         source: function(request, response) {
   219         source: function(request, response) {
   154             var result = new Array();
   220             var result = [];
   155             var presult = new Array();
   221             var newResults = [];
   156             var tresult = new Array();
   222 
   157             var mresult = new Array();
   223             searchPattern = makeCamelCaseRegex(request.term);
   158             var tgresult = new Array();
   224             if (searchPattern === "") {
   159             var secondaryresult = new Array();
   225                 return this.close();
   160             var displayCount = 0;
   226             }
   161             var exactMatcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(request.term) + "$", "i");
   227             var camelCaseMatcher = createMatcher(searchPattern, "");
   162             camelCaseRegexp = ($.ui.autocomplete.escapeRegex(request.term)).split(/(?=[A-Z])/).join("([a-z0-9_$]*?)");
   228             var boundaryMatcher = createMatcher("\\b" + searchPattern, "");
   163             var camelCaseMatcher = new RegExp("^" + camelCaseRegexp);
       
   164             secondaryMatcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
       
   165 
       
   166             // Return the nested innermost name from the specified object
       
   167             function nestedName(e) {
       
   168                 return e.l.substring(e.l.lastIndexOf(".") + 1);
       
   169             }
       
   170 
   229 
   171             function concatResults(a1, a2) {
   230             function concatResults(a1, a2) {
   172                 a1 = a1.concat(a2);
   231                 a2.sort(function(e1, e2) {
       
   232                     return e1.ranking - e2.ranking;
       
   233                 });
       
   234                 a1 = a1.concat(a2.map(function(e) { return e.item; }));
   173                 a2.length = 0;
   235                 a2.length = 0;
   174                 return a1;
   236                 return a1;
   175             }
   237             }
   176 
   238 
   177             if (moduleSearchIndex) {
   239             if (moduleSearchIndex) {
   178                 var mdleCount = 0;
       
   179                 $.each(moduleSearchIndex, function(index, item) {
   240                 $.each(moduleSearchIndex, function(index, item) {
   180                     item.category = catModules;
   241                     item.category = catModules;
   181                     if (exactMatcher.test(item.l)) {
   242                     var ranking = rankMatch(boundaryMatcher.exec(item.l), catModules);
   182                         result.push(item);
   243                     if (ranking < RANKING_THRESHOLD) {
   183                         mdleCount++;
   244                         newResults.push({ ranking: ranking, item: item });
   184                     } else if (camelCaseMatcher.test(item.l)) {
   245                     }
   185                         result.push(item);
   246                     return newResults.length < MAX_RESULTS_PER_CATEGORY;
   186                     } else if (secondaryMatcher.test(item.l)) {
   247                 });
   187                         secondaryresult.push(item);
   248                 result = concatResults(result, newResults);
   188                     }
       
   189                 });
       
   190                 displayCount = mdleCount;
       
   191                 result = concatResults(result, secondaryresult);
       
   192             }
   249             }
   193             if (packageSearchIndex) {
   250             if (packageSearchIndex) {
   194                 var pCount = 0;
       
   195                 var pkg = "";
       
   196                 $.each(packageSearchIndex, function(index, item) {
   251                 $.each(packageSearchIndex, function(index, item) {
   197                     item.category = catPackages;
   252                     item.category = catPackages;
   198                     pkg = (item.m)
   253                     var name = (item.m && request.term.indexOf("/") > -1)
   199                             ? (item.m + "/" + item.l)
   254                             ? (item.m + "/" + item.l)
   200                             : item.l;
   255                             : item.l;
   201                     if (exactMatcher.test(item.l)) {
   256                     var ranking = rankMatch(boundaryMatcher.exec(name), catPackages);
   202                         presult.push(item);
   257                     if (ranking < RANKING_THRESHOLD) {
   203                         pCount++;
   258                         newResults.push({ ranking: ranking, item: item });
   204                     } else if (camelCaseMatcher.test(pkg)) {
   259                     }
   205                         presult.push(item);
   260                     return newResults.length < MAX_RESULTS_PER_CATEGORY;
   206                     } else if (secondaryMatcher.test(pkg)) {
   261                 });
   207                         secondaryresult.push(item);
   262                 result = concatResults(result, newResults);
   208                     }
       
   209                 });
       
   210                 result = result.concat(concatResults(presult, secondaryresult));
       
   211                 displayCount = (pCount > displayCount) ? pCount : displayCount;
       
   212             }
   263             }
   213             if (typeSearchIndex) {
   264             if (typeSearchIndex) {
   214                 var tCount = 0;
       
   215                 $.each(typeSearchIndex, function(index, item) {
   265                 $.each(typeSearchIndex, function(index, item) {
   216                     item.category = catTypes;
   266                     item.category = catTypes;
   217                     var s = nestedName(item);
   267                     var name = request.term.indexOf(".") > -1
   218                     if (exactMatcher.test(s)) {
   268                         ? item.p + "." + item.l
   219                         tresult.push(item);
   269                         : item.l;
   220                         tCount++;
   270                     var ranking = rankMatch(camelCaseMatcher.exec(name), catTypes);
   221                     } else if (camelCaseMatcher.test(s)) {
   271                     if (ranking < RANKING_THRESHOLD) {
   222                         tresult.push(item);
   272                         newResults.push({ ranking: ranking, item: item });
   223                     } else if (secondaryMatcher.test(item.p + "." + item.l)) {
   273                     }
   224                         secondaryresult.push(item);
   274                     return newResults.length < MAX_RESULTS_PER_CATEGORY;
   225                     }
   275                 });
   226                 });
   276                 result = concatResults(result, newResults);
   227                 result = result.concat(concatResults(tresult, secondaryresult));
       
   228                 displayCount = (tCount > displayCount) ? tCount : displayCount;
       
   229             }
   277             }
   230             if (memberSearchIndex) {
   278             if (memberSearchIndex) {
   231                 var mCount = 0;
       
   232                 $.each(memberSearchIndex, function(index, item) {
   279                 $.each(memberSearchIndex, function(index, item) {
   233                     item.category = catMembers;
   280                     item.category = catMembers;
   234                     var s = nestedName(item);
   281                     var name = request.term.indexOf(".") > -1
   235                     if (exactMatcher.test(s)) {
   282                             ? item.p + "." + item.c + "." + item.l
   236                         mresult.push(item);
   283                             : item.l;
   237                         mCount++;
   284                     var ranking = rankMatch(camelCaseMatcher.exec(name), catMembers);
   238                     } else if (camelCaseMatcher.test(s)) {
   285                     if (ranking < RANKING_THRESHOLD) {
   239                         mresult.push(item);
   286                         newResults.push({ ranking: ranking, item: item });
   240                     } else if (secondaryMatcher.test(item.c + "." + item.l)) {
   287                     }
   241                         secondaryresult.push(item);
   288                     return newResults.length < MAX_RESULTS_PER_CATEGORY;
   242                     }
   289                 });
   243                 });
   290                 result = concatResults(result, newResults);
   244                 result = result.concat(concatResults(mresult, secondaryresult));
       
   245                 displayCount = (mCount > displayCount) ? mCount : displayCount;
       
   246             }
   291             }
   247             if (tagSearchIndex) {
   292             if (tagSearchIndex) {
   248                 var tgCount = 0;
       
   249                 $.each(tagSearchIndex, function(index, item) {
   293                 $.each(tagSearchIndex, function(index, item) {
   250                     item.category = catSearchTags;
   294                     item.category = catSearchTags;
   251                     if (exactMatcher.test(item.l)) {
   295                     var ranking = rankMatch(boundaryMatcher.exec(item.l), catSearchTags);
   252                         tgresult.push(item);
   296                     if (ranking < RANKING_THRESHOLD) {
   253                         tgCount++;
   297                         newResults.push({ ranking: ranking, item: item });
   254                     } else if (secondaryMatcher.test(item.l)) {
   298                     }
   255                         secondaryresult.push(item);
   299                     return newResults.length < MAX_RESULTS_PER_CATEGORY;
   256                     }
   300                 });
   257                 });
   301                 result = concatResults(result, newResults);
   258                 result = result.concat(concatResults(tgresult, secondaryresult));
   302             }
   259                 displayCount = (tgCount > displayCount) ? tgCount : displayCount;
   303             response(result);
   260             }
       
   261             displayCount = (displayCount > 500) ? displayCount : 500;
       
   262             var counter = function() {
       
   263                 var count = {Modules: 0, Packages: 0, Types: 0, Members: 0, SearchTags: 0};
       
   264                 var f = function(item) {
       
   265                     count[item.category] += 1;
       
   266                     return (count[item.category] <= displayCount);
       
   267                 };
       
   268                 return f;
       
   269             }();
       
   270             response(result.filter(counter));
       
   271         },
   304         },
   272         response: function(event, ui) {
   305         response: function(event, ui) {
   273             if (!ui.content.length) {
   306             if (!ui.content.length) {
   274                 ui.content.push(noResult);
   307                 ui.content.push(noResult);
   275             } else {
   308             } else {