search-page.js (12825B)
1 /* 2 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ 6 */ 7 8 "use strict"; 9 $(function() { 10 var copy = $("#page-search-copy"); 11 var expand = $("#page-search-expand"); 12 var searchLink = $("span#page-search-link"); 13 var redirect = $("input#search-redirect"); 14 function setSearchUrlTemplate() { 15 var href = document.location.href.split(/[#?]/)[0]; 16 href += "?q=" + "%s"; 17 if (redirect.is(":checked")) { 18 href += "&r=1"; 19 } 20 searchLink.html(href); 21 copy[0].onmouseenter(); 22 } 23 function copyLink(e) { 24 copyToClipboard(this.previousSibling.innerText); 25 switchCopyLabel(this, this.lastElementChild); 26 } 27 copy.on("click", copyLink); 28 copy[0].onmouseenter = function() {}; 29 redirect.on("click", setSearchUrlTemplate); 30 setSearchUrlTemplate(); 31 copy.prop("disabled", false); 32 redirect.prop("disabled", false); 33 expand.on("click", function (e) { 34 var searchInfo = $("div.page-search-info"); 35 if(this.parentElement.hasAttribute("open")) { 36 searchInfo.attr("style", " display:none;"); 37 } else { 38 searchInfo.attr("style", "display:block;"); 39 } 40 }); 41 }); 42 $(window).on("load", function() { 43 var input = $("#page-search-input"); 44 var reset = $("#page-search-reset"); 45 var modules = $("#search-modules"); 46 var notify = $("#page-search-notify"); 47 var resultSection = $("div#result-section"); 48 var resultContainer = $("div#result-container"); 49 var selectedLink; 50 var searchTerm = ""; 51 var activeTab = ""; 52 var fixedTab = false; 53 var visibleTabs = []; 54 var feelingLucky = false; 55 const MIN_TABBED_RESULTS = 10; 56 function renderResults(result) { 57 if (!result.length) { 58 notify.html(messages.noResult); 59 } else if (result.length === 1) { 60 notify.html(messages.oneResult); 61 } else { 62 notify.html(messages.manyResults.replace("{0}", result.length)); 63 } 64 resultContainer.empty(); 65 var r = { 66 "types": [], 67 "members": [], 68 "packages": [], 69 "modules": [], 70 "searchTags": [] 71 }; 72 for (var i in result) { 73 var item = result[i]; 74 var arr = r[item.category]; 75 arr.push(item); 76 } 77 if (!activeTab || r[activeTab].length === 0) { 78 activeTab = Object.keys(r).find(category => r[category].length > 0); 79 } 80 if (feelingLucky && activeTab) { 81 notify.html(messages.redirecting) 82 var firstItem = r[activeTab][0]; 83 window.location = getURL(firstItem.indexItem, firstItem.category); 84 return; 85 } 86 if (searchTerm.endsWith(".") && result.length > MIN_TABBED_RESULTS) { 87 if (activeTab === "types" && r["members"].length > r["types"].length) { 88 activeTab = "members"; 89 } else if (activeTab === "packages" && r["types"].length > r["packages"].length) { 90 activeTab = "types"; 91 } 92 } 93 var categoryCount = Object.keys(r).reduce(function(prev, curr) { 94 return prev + (r[curr].length > 0 ? 1 : 0); 95 }, 0); 96 visibleTabs = []; 97 var tabContainer = $("<div class='table-tabs'></div>").appendTo(resultContainer); 98 for (var key in r) { 99 var id = "#result-tab-" + key.replace("searchTags", "search_tags"); 100 if (r[key].length) { 101 var count = r[key].length >= 1000 ? "999+" : r[key].length; 102 if (result.length > MIN_TABBED_RESULTS && categoryCount > 1) { 103 let button = $("<button/>") 104 .attr("id", "result-tab-" + key) 105 .attr("tabIndex", "-1") 106 .addClass("page-search-header") 107 .append($("<span/>") 108 .html(categories[key]) 109 .append($("<span/>") 110 .attr("style", "font-weight:normal;") 111 .html(" (" + count + ")"))) 112 .on("click", null, key, function(e) { 113 fixedTab = true; 114 renderResult(e.data, $(this)); 115 }).appendTo(tabContainer); 116 visibleTabs.push(key); 117 } else { 118 $("<span class='page-search-header'>" + categories[key] 119 + "<span style='font-weight: normal'> (" + count + ")</span></span>").appendTo(tabContainer); 120 renderTable(key, r[key]).appendTo(resultContainer); 121 tabContainer = $("<div class='table-tabs'></div>").appendTo(resultContainer); 122 } 123 } 124 } 125 if (activeTab && result.length > MIN_TABBED_RESULTS && categoryCount > 1) { 126 $("button#result-tab-" + activeTab).addClass("active-table-tab").attr("tabIndex", "0"); 127 renderTable(activeTab, r[activeTab]).appendTo(resultContainer); 128 } 129 resultSection.show(); 130 function renderResult(category, button) { 131 activeTab = category; 132 setSearchUrl(); 133 resultContainer.find("div.result-table").remove(); 134 renderTable(activeTab, r[activeTab]).appendTo(resultContainer); 135 button.siblings().removeClass("active-table-tab").attr("tabIndex", "-1"); 136 button.addClass("active-table-tab").attr("tabIndex", "0"); 137 } 138 } 139 function selectTab(category) { 140 $("button#result-tab-" + category).focus().trigger("click"); 141 } 142 function renderTable(category, items) { 143 var table = $("<div class='result-table'>"); 144 var col1, col2; 145 if (category === "modules") { 146 col1 = mdlDesc; 147 } else if (category === "packages") { 148 col1 = pkgDesc; 149 } else if (category === "types") { 150 col1 = clsDesc; 151 } else if (category === "members") { 152 col1 = mbrDesc; 153 } else if (category === "searchTags") { 154 col1 = tagDesc; 155 } 156 col2 = descDesc; 157 $("<div class='table-header'/>") 158 .append($("<span class='table-header'/>").html(col1)) 159 .append($("<span class='table-header'/>").html(col2)) 160 .appendTo(table); 161 $.each(items, function(index, item) { 162 renderItem(item, table); 163 }); 164 return table; 165 } 166 function select() { 167 if (!this.classList.contains("selected")) { 168 setSelected(this); 169 } 170 } 171 function unselect() { 172 if (this.classList.contains("selected")) { 173 setSelected(null); 174 } 175 } 176 function renderItem(item, table) { 177 var label = getResultLabel(item); 178 var desc = getResultDescription(item); 179 var link = $("<a/>") 180 .attr("href", getURL(item.indexItem, item.category)) 181 .attr("tabindex", "0") 182 .addClass("search-result-link"); 183 link.on("mousemove", select.bind(link[0])) 184 .on("focus", select.bind(link[0])) 185 .on("mouseleave", unselect.bind(link[0])) 186 .on("blur", unselect.bind(link[0])) 187 .append($("<span/>").addClass("search-result-label").html(label)) 188 .append($("<span/>").addClass("search-result-desc").html(desc)) 189 .appendTo(table); 190 } 191 var timeout; 192 function schedulePageSearch() { 193 if (timeout) { 194 clearTimeout(timeout); 195 } 196 timeout = setTimeout(function () { 197 doPageSearch() 198 }, 100); 199 } 200 function doPageSearch() { 201 setSearchUrl(); 202 var term = searchTerm = input.val().trim(); 203 if (term === "") { 204 notify.html(messages.enterTerm); 205 activeTab = ""; 206 fixedTab = false; 207 resultContainer.empty(); 208 resultSection.hide(); 209 } else { 210 notify.html(messages.searching); 211 var module = modules.val(); 212 doSearch({ term: term, maxResults: 1200, module: module}, renderResults); 213 } 214 } 215 function setSearchUrl() { 216 var query = input.val().trim(); 217 var url = document.location.pathname; 218 if (query) { 219 url += "?q=" + encodeURI(query); 220 if (activeTab && fixedTab) { 221 url += "&c=" + activeTab; 222 } 223 if (modules.val()) { 224 url += "&m=" + modules.val(); 225 } 226 } 227 history.replaceState({query: query}, "", url); 228 } 229 input.on("input", function(e) { 230 feelingLucky = false; 231 reset.css("visibility", input.val() ? "visible" : "hidden"); 232 schedulePageSearch(); 233 }); 234 function setSelected(link) { 235 if (selectedLink) { 236 selectedLink.classList.remove("selected"); 237 selectedLink.blur(); 238 } 239 if (link) { 240 link.classList.add("selected"); 241 link.focus({focusVisible: true}); 242 } 243 selectedLink = link; 244 } 245 document.addEventListener("keydown", e => { 246 if (e.ctrlKey || e.altKey || e.metaKey) { 247 return; 248 } 249 if (e.key === "Escape" && input.val()) { 250 input.val("").focus(); 251 doPageSearch(); 252 e.preventDefault(); 253 } 254 if (e.target === modules[0]) { 255 return; 256 } 257 if (e.key === "ArrowLeft" || e.key === "ArrowRight") { 258 if (activeTab && visibleTabs.length > 1 && e.target !== input[0]) { 259 var tab = visibleTabs.indexOf(activeTab); 260 var newTab = e.key === "ArrowLeft" 261 ? Math.max(0, tab - 1) 262 : Math.min(visibleTabs.length - 1, tab + 1); 263 if (newTab !== tab) { 264 selectTab(visibleTabs[newTab]); 265 } 266 e.preventDefault(); 267 } 268 } else if (e.key === "ArrowUp" || e.key === "ArrowDown") { 269 let links = Array.from( 270 document.querySelectorAll("div.result-table > a.search-result-link")); 271 let current = links.indexOf(selectedLink); 272 let activeButton = document.querySelector("button.active-table-tab"); 273 if (e.key === "ArrowUp" || (e.key === "Tab" && e.shiftKey)) { 274 if (current > 0) { 275 setSelected(links[current - 1]); 276 } else { 277 setSelected(null); 278 if (activeButton && current === 0) { 279 activeButton.focus(); 280 } else { 281 input.focus(); 282 } 283 } 284 } else if (e.key === "ArrowDown") { 285 if (document.activeElement === input[0] && activeButton) { 286 activeButton.focus(); 287 } else if (current < links.length - 1) { 288 setSelected(links[current + 1]); 289 } 290 } 291 e.preventDefault(); 292 } else if (e.key.length === 1 || e.key === "Backspace") { 293 setSelected(null); 294 input.focus(); 295 } 296 }); 297 reset.on("click", function() { 298 notify.html(messages.enterTerm); 299 resultSection.hide(); 300 activeTab = ""; 301 fixedTab = false; 302 resultContainer.empty(); 303 input.val('').focus(); 304 setSearchUrl(); 305 }); 306 modules.on("change", function() { 307 if (input.val()) { 308 doPageSearch(); 309 } 310 input.focus(); 311 try { 312 localStorage.setItem("search-modules", modules.val()); 313 } catch (unsupported) {} 314 }); 315 316 input.prop("disabled", false); 317 input.attr("autocapitalize", "off"); 318 reset.prop("disabled", false); 319 320 var urlParams = new URLSearchParams(window.location.search); 321 if (urlParams.has("m")) { 322 modules.val(urlParams.get("m")); 323 } else { 324 try { 325 var searchModules = localStorage.getItem("search-modules"); 326 if (searchModules) { 327 modules.val(searchModules); 328 } 329 } catch (unsupported) {} 330 } 331 if (urlParams.has("q")) { 332 input.val(urlParams.get("q")); 333 reset.css("visibility", input.val() ? "visible" : "hidden"); 334 } 335 if (urlParams.has("c")) { 336 activeTab = urlParams.get("c"); 337 fixedTab = true; 338 } 339 if (urlParams.get("r")) { 340 feelingLucky = true; 341 } 342 if (input.val()) { 343 doPageSearch(); 344 } else { 345 notify.html(messages.enterTerm); 346 } 347 input.select().focus(); 348 });