1/* 2 * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 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 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26var noResult = {l: "##REPLACE:doclet.search.no_results##"}; 27var loading = {l: "##REPLACE:doclet.search.loading##"}; 28var catModules = "##REPLACE:doclet.search.modules##"; 29var catPackages = "##REPLACE:doclet.search.packages##"; 30var catTypes = "##REPLACE:doclet.search.types##"; 31var catMembers = "##REPLACE:doclet.search.members##"; 32var catSearchTags = "##REPLACE:doclet.search.search_tags##"; 33var highlight = "<span class=\"result-highlight\">$&</span>"; 34var searchPattern = ""; 35var fallbackPattern = ""; 36var RANKING_THRESHOLD = 2; 37var NO_MATCH = 0xffff; 38var MIN_RESULTS = 3; 39var MAX_RESULTS = 500; 40var UNNAMED = "<Unnamed>"; 41function escapeHtml(str) { 42 return str.replace(/</g, "<").replace(/>/g, ">"); 43} 44function getHighlightedText(item, matcher, fallbackMatcher) { 45 var escapedItem = escapeHtml(item); 46 var highlighted = escapedItem.replace(matcher, highlight); 47 if (highlighted === escapedItem) { 48 highlighted = escapedItem.replace(fallbackMatcher, highlight) 49 } 50 return highlighted; 51} 52function getURLPrefix(ui) { 53 var urlPrefix=""; 54 var slash = "/"; 55 if (ui.item.category === catModules) { 56 return ui.item.l + slash; 57 } else if (ui.item.category === catPackages && ui.item.m) { 58 return ui.item.m + slash; 59 } else if (ui.item.category === catTypes || ui.item.category === catMembers) { 60 if (ui.item.m) { 61 urlPrefix = ui.item.m + slash; 62 } else { 63 $.each(packageSearchIndex, function(index, item) { 64 if (item.m && ui.item.p === item.l) { 65 urlPrefix = item.m + slash; 66 } 67 }); 68 } 69 } 70 return urlPrefix; 71} 72function createSearchPattern(term) { 73 var pattern = ""; 74 var isWordToken = false; 75 term.replace(/,\s*/g, ", ").trim().split(/\s+/).forEach(function(w, index) { 76 if (index > 0) { 77 // whitespace between identifiers is significant 78 pattern += (isWordToken && /^\w/.test(w)) ? "\\s+" : "\\s*"; 79 } 80 var tokens = w.split(/(?=[A-Z,.()<>[\/])/); 81 for (var i = 0; i < tokens.length; i++) { 82 var s = tokens[i]; 83 if (s === "") { 84 continue; 85 } 86 pattern += $.ui.autocomplete.escapeRegex(s); 87 isWordToken = /\w$/.test(s); 88 if (isWordToken) { 89 pattern += "([a-z0-9_$<>\\[\\]]*?)"; 90 } 91 } 92 }); 93 return pattern; 94} 95function createMatcher(pattern, flags) { 96 var isCamelCase = /[A-Z]/.test(pattern); 97 return new RegExp(pattern, flags + (isCamelCase ? "" : "i")); 98} 99var watermark = 'Search'; 100$(function() { 101 var search = $("#search"); 102 var reset = $("#reset"); 103 search.val(''); 104 search.prop("disabled", false); 105 reset.prop("disabled", false); 106 search.val(watermark).addClass('watermark'); 107 search.blur(function() { 108 if ($(this).val().length === 0) { 109 $(this).val(watermark).addClass('watermark'); 110 } 111 }); 112 search.on('click keydown paste', function() { 113 if ($(this).val() === watermark) { 114 $(this).val('').removeClass('watermark'); 115 } 116 }); 117 reset.click(function() { 118 search.val('').focus(); 119 }); 120 search.focus()[0].setSelectionRange(0, 0); 121}); 122$.widget("custom.catcomplete", $.ui.autocomplete, { 123 _create: function() { 124 this._super(); 125 this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)"); 126 }, 127 _renderMenu: function(ul, items) { 128 var rMenu = this; 129 var currentCategory = ""; 130 rMenu.menu.bindings = $(); 131 $.each(items, function(index, item) { 132 var li; 133 if (item.category && item.category !== currentCategory) { 134 ul.append("<li class=\"ui-autocomplete-category\">" + item.category + "</li>"); 135 currentCategory = item.category; 136 } 137 li = rMenu._renderItemData(ul, item); 138 if (item.category) { 139 li.attr("aria-label", item.category + " : " + item.l); 140 li.attr("class", "result-item"); 141 } else { 142 li.attr("aria-label", item.l); 143 li.attr("class", "result-item"); 144 } 145 }); 146 }, 147 _renderItem: function(ul, item) { 148 var label = ""; 149 var matcher = createMatcher(escapeHtml(searchPattern), "g"); 150 var fallbackMatcher = new RegExp(fallbackPattern, "gi") 151 if (item.category === catModules) { 152 label = getHighlightedText(item.l, matcher, fallbackMatcher); 153 } else if (item.category === catPackages) { 154 label = getHighlightedText(item.l, matcher, fallbackMatcher); 155 } else if (item.category === catTypes) { 156 label = (item.p && item.p !== UNNAMED) 157 ? getHighlightedText(item.p + "." + item.l, matcher, fallbackMatcher) 158 : getHighlightedText(item.l, matcher, fallbackMatcher); 159 } else if (item.category === catMembers) { 160 label = (item.p && item.p !== UNNAMED) 161 ? getHighlightedText(item.p + "." + item.c + "." + item.l, matcher, fallbackMatcher) 162 : getHighlightedText(item.c + "." + item.l, matcher, fallbackMatcher); 163 } else if (item.category === catSearchTags) { 164 label = getHighlightedText(item.l, matcher, fallbackMatcher); 165 } else { 166 label = item.l; 167 } 168 var li = $("<li/>").appendTo(ul); 169 var div = $("<div/>").appendTo(li); 170 if (item.category === catSearchTags && item.h) { 171 if (item.d) { 172 div.html(label + "<span class=\"search-tag-holder-result\"> (" + item.h + ")</span><br><span class=\"search-tag-desc-result\">" 173 + item.d + "</span><br>"); 174 } else { 175 div.html(label + "<span class=\"search-tag-holder-result\"> (" + item.h + ")</span>"); 176 } 177 } else { 178 if (item.m) { 179 div.html(item.m + "/" + label); 180 } else { 181 div.html(label); 182 } 183 } 184 return li; 185 } 186}); 187function rankMatch(match, category) { 188 if (!match) { 189 return NO_MATCH; 190 } 191 var index = match.index; 192 var input = match.input; 193 var leftBoundaryMatch = 2; 194 var periferalMatch = 0; 195 // make sure match is anchored on a left word boundary 196 if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index]) { 197 leftBoundaryMatch = 0; 198 } else if ("_" === input[index - 1] || (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input))) { 199 leftBoundaryMatch = 1; 200 } 201 var matchEnd = index + match[0].length; 202 var leftParen = input.indexOf("("); 203 var endOfName = leftParen > -1 ? leftParen : input.length; 204 // exclude peripheral matches 205 if (category !== catModules && category !== catSearchTags) { 206 var delim = category === catPackages ? "/" : "."; 207 if (leftParen > -1 && leftParen < index) { 208 periferalMatch += 2; 209 } else if (input.lastIndexOf(delim, endOfName) >= matchEnd) { 210 periferalMatch += 2; 211 } 212 } 213 var delta = match[0].length === endOfName ? 0 : 1; // rank full match higher than partial match 214 for (var i = 1; i < match.length; i++) { 215 // lower ranking if parts of the name are missing 216 if (match[i]) 217 delta += match[i].length; 218 } 219 if (category === catTypes) { 220 // lower ranking if a type name contains unmatched camel-case parts 221 if (/[A-Z]/.test(input.substring(matchEnd))) 222 delta += 5; 223 if (/[A-Z]/.test(input.substring(0, index))) 224 delta += 5; 225 } 226 return leftBoundaryMatch + periferalMatch + (delta / 200); 227 228} 229function doSearch(request, response) { 230 var result = []; 231 searchPattern = createSearchPattern(request.term); 232 fallbackPattern = createSearchPattern(request.term.toLowerCase()); 233 if (searchPattern === "") { 234 return this.close(); 235 } 236 var camelCaseMatcher = createMatcher(searchPattern, ""); 237 var fallbackMatcher = new RegExp(fallbackPattern, "i"); 238 239 function searchIndexWithMatcher(indexArray, matcher, category, nameFunc) { 240 if (indexArray) { 241 var newResults = []; 242 $.each(indexArray, function (i, item) { 243 item.category = category; 244 var ranking = rankMatch(matcher.exec(nameFunc(item)), category); 245 if (ranking < RANKING_THRESHOLD) { 246 newResults.push({ranking: ranking, item: item}); 247 } 248 return newResults.length <= MAX_RESULTS; 249 }); 250 return newResults.sort(function(e1, e2) { 251 return e1.ranking - e2.ranking; 252 }).map(function(e) { 253 return e.item; 254 }); 255 } 256 return []; 257 } 258 function searchIndex(indexArray, category, nameFunc) { 259 var primaryResults = searchIndexWithMatcher(indexArray, camelCaseMatcher, category, nameFunc); 260 result = result.concat(primaryResults); 261 if (primaryResults.length <= MIN_RESULTS && camelCaseMatcher.flags.indexOf("i") === -1) { 262 var secondaryResults = searchIndexWithMatcher(indexArray, fallbackMatcher, category, nameFunc); 263 result = result.concat(secondaryResults.filter(function (item) { 264 return primaryResults.indexOf(item) === -1; 265 })); 266 } 267 } 268 269 searchIndex(moduleSearchIndex, catModules, function(item) { return item.l; }); 270 searchIndex(packageSearchIndex, catPackages, function(item) { 271 return (item.m && request.term.indexOf("/") > -1) 272 ? (item.m + "/" + item.l) : item.l; 273 }); 274 searchIndex(typeSearchIndex, catTypes, function(item) { 275 return request.term.indexOf(".") > -1 ? item.p + "." + item.l : item.l; 276 }); 277 searchIndex(memberSearchIndex, catMembers, function(item) { 278 return request.term.indexOf(".") > -1 279 ? item.p + "." + item.c + "." + item.l : item.l; 280 }); 281 searchIndex(tagSearchIndex, catSearchTags, function(item) { return item.l; }); 282 283 if (!indexFilesLoaded()) { 284 updateSearchResults = function() { 285 doSearch(request, response); 286 } 287 result.unshift(loading); 288 } else { 289 updateSearchResults = function() {}; 290 } 291 response(result); 292} 293$(function() { 294 $("#search").catcomplete({ 295 minLength: 1, 296 delay: 300, 297 source: doSearch, 298 response: function(event, ui) { 299 if (!ui.content.length) { 300 ui.content.push(noResult); 301 } else { 302 $("#search").empty(); 303 } 304 }, 305 autoFocus: true, 306 focus: function(event, ui) { 307 return false; 308 }, 309 position: { 310 collision: "flip" 311 }, 312 select: function(event, ui) { 313 if (ui.item.category) { 314 var url = getURLPrefix(ui); 315 if (ui.item.category === catModules) { 316 url += "module-summary.html"; 317 } else if (ui.item.category === catPackages) { 318 if (ui.item.u) { 319 url = ui.item.u; 320 } else { 321 url += ui.item.l.replace(/\./g, '/') + "/package-summary.html"; 322 } 323 } else if (ui.item.category === catTypes) { 324 if (ui.item.u) { 325 url = ui.item.u; 326 } else if (ui.item.p === UNNAMED) { 327 url += ui.item.l + ".html"; 328 } else { 329 url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.l + ".html"; 330 } 331 } else if (ui.item.category === catMembers) { 332 if (ui.item.p === UNNAMED) { 333 url += ui.item.c + ".html" + "#"; 334 } else { 335 url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.c + ".html" + "#"; 336 } 337 if (ui.item.u) { 338 url += ui.item.u; 339 } else { 340 url += ui.item.l; 341 } 342 } else if (ui.item.category === catSearchTags) { 343 url += ui.item.u; 344 } 345 if (top !== window) { 346 parent.classFrame.location = pathtoroot + url; 347 } else { 348 window.location.href = pathtoroot + url; 349 } 350 $("#search").focus(); 351 } 352 } 353 }); 354}); 355