aboutsummaryrefslogtreecommitdiff
path: root/plugins/javadoc/src/main/resources/static_res/search.js
diff options
context:
space:
mode:
authorMarcin Aman <maman@virtuslab.com>2020-07-09 10:45:06 +0200
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-07-16 13:17:39 +0200
commit04cf1cdd3a909fa62e5ffda5b7d04695c749177e (patch)
tree5c38372dd70f5528bf55c084a1893a7af8e41359 /plugins/javadoc/src/main/resources/static_res/search.js
parent42c6bcdbd564628907491289555c6d6713294fef (diff)
downloaddokka-04cf1cdd3a909fa62e5ffda5b7d04695c749177e.tar.gz
dokka-04cf1cdd3a909fa62e5ffda5b7d04695c749177e.tar.bz2
dokka-04cf1cdd3a909fa62e5ffda5b7d04695c749177e.zip
Implement javadoc search
Diffstat (limited to 'plugins/javadoc/src/main/resources/static_res/search.js')
-rw-r--r--plugins/javadoc/src/main/resources/static_res/search.js208
1 files changed, 208 insertions, 0 deletions
diff --git a/plugins/javadoc/src/main/resources/static_res/search.js b/plugins/javadoc/src/main/resources/static_res/search.js
new file mode 100644
index 00000000..1608e648
--- /dev/null
+++ b/plugins/javadoc/src/main/resources/static_res/search.js
@@ -0,0 +1,208 @@
+const constants = {
+ noResult: {
+ l: "No results found",
+ renderable: "No results found"
+ },
+ labels: {
+ modules: 'Modules',
+ packages: 'Packages',
+ types: 'Types',
+ members: 'Members',
+ tags: 'SearchTags'
+ }
+}
+
+//It is super important to have vars here since they are lifter outside the block
+//ES6 syntax doesn't provide those feature and therefore will fail when one of those values wouldn't be initialized
+//eg. when a request for a given package fails
+if(typeof moduleSearchIndex === 'undefined'){
+ var moduleSearchIndex;
+}
+if(typeof packageSearchIndex === 'undefined'){
+ var packageSearchIndex;
+}
+if(typeof typeSearchIndex === 'undefined'){
+ var typeSearchIndex;
+}
+if(typeof memberSearchIndex === 'undefined'){
+ var memberSearchIndex;
+}
+if(typeof tagSearchIndex === 'undefined'){
+ var tagSearchIndex;
+}
+
+const clearElementValue = (element) => {
+ element.val('')
+}
+
+$(function init() {
+ const search = $("#search")
+ const reset = $("#reset")
+
+ clearElementValue(search)
+ reset.on('click', () => {
+ clearElementValue(search)
+ search.focus()
+ })
+})
+
+const itemHasResults = (item) => {
+ return item.l !== constants.noResult
+}
+
+$.widget("custom.catcomplete", $.ui.autocomplete, {
+ _create: function() {
+ this._super();
+ },
+ _renderMenu: function(ul, items) {
+ const menu = this;
+ let category
+ $.each(items, (index, item) => {
+ const shouldCategoryLabelBeRendered = itemHasResults(item) && item.category !== category
+ if (shouldCategoryLabelBeRendered) {
+ ul.append(`<li class="ui-autocomplete-category">${item.category}</li>`);
+ category = item.category;
+ }
+
+ const li = menu._renderItemData(ul, item);
+ if (item.category) {
+ li.attr("aria-label", `${item.category} : ${item.l}`);
+ } else {
+ li.attr("aria-label", item.l);
+ }
+ li.attr("class", "resultItem");
+ });
+ },
+ _renderItem: (ul, item) => {
+ const li = $("<li/>").appendTo(ul);
+ const div = $("<div/>").appendTo(li);
+ div.html(item.renderable);
+ return li;
+ }
+});
+
+const highlight = (match) => `<span class="resultHighlight">` + match + `</span>`
+const escapeHtml = (str) => str.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+
+const labelForPackage = (element) => (element.m) ? (element.m + "/" + element.l) : element.l
+const labelForNested = (element) => {
+ var label = ""
+ if(element.p) label += `${element.p}.`
+ if(element.l !== element.c && element.c) label += `${element.c}.`
+ return label + element.l
+}
+const nestedName = (e) => e.l.substring(e.l.lastIndexOf(".") + 1)
+
+const renderableFromLabel = (label, regex) => escapeHtml(label).replace(regex, highlight)
+
+$(() => {
+ $("#search").catcomplete({
+ minLength: 1,
+ delay: 100,
+ source: function(request, response) {
+ const exactRegexp = $.ui.autocomplete.escapeRegex(request.term) + "$"
+ const exactMatcher = new RegExp("^" + exactRegexp, "i");
+ const camelCaseRegexp = ($.ui.autocomplete.escapeRegex(request.term)).split(/(?=[A-Z])/).join("([a-z0-9_$]*?)");
+ const camelCaseMatcher = new RegExp("^" + camelCaseRegexp);
+ const secondaryMatcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
+
+ const processWithExactLabel = (dataset, category) => {
+ const exactOrCamelMatches = []
+ const secondaryMatches = []
+
+ dataset.map(element => {
+ element.category = category
+ return element
+ }).forEach((element) => {
+ if(exactMatcher.test(element.l)){
+ element.renderable = renderableFromLabel(element.l, exactMatcher)
+ exactOrCamelMatches.push(element)
+ } else if(camelCaseMatcher.test(element.l)){
+ element.renderable = renderableFromLabel(element.l, camelCaseMatcher)
+ exactOrCamelMatches.push(element)
+ } else if(secondaryMatcher.test(element.l)){
+ element.renderable = renderableFromLabel(element.l, secondaryMatcher)
+ secondaryMatches.push(element)
+ }
+ })
+
+ return [...exactOrCamelMatches, ...secondaryMatches]
+ }
+
+ const processPackages = (dataset) => {
+ const exactOrCamelMatches = []
+ const secondaryMatches = []
+
+ dataset.map(element => {
+ element.category = constants.labels.packages
+ return element
+ }).forEach((element) => {
+ const label = labelForPackage(element);
+ if(exactMatcher.test(element.l)){
+ element.renderable = renderableFromLabel(element.l, exactMatcher)
+ exactOrCamelMatches.push(element)
+ } else if(camelCaseMatcher.test(label)){
+ element.renderable = renderableFromLabel(label, camelCaseMatcher)
+ exactOrCamelMatches.push(element)
+ } else if(secondaryMatcher.test(label)){
+ element.renderable = renderableFromLabel(label, secondaryMatcher)
+ secondaryMatches.push(element)
+ }
+ })
+
+ return [...exactOrCamelMatches, ...secondaryMatches]
+ }
+
+ const processNested = (dataset, label) => {
+ const exactOrCamelMatches = []
+ const secondaryMatches = []
+
+ dataset.map(element => {
+ element.category = label
+ return element
+ }).forEach((element) => {
+ const label = nestedName(element);
+ if(exactMatcher.test(label)) {
+ element.renderable = renderableFromLabel(labelForNested(element), new RegExp(exactRegexp, "i"))
+ exactOrCamelMatches.push(element)
+ } else if(camelCaseMatcher.test(label)){
+ element.renderable = renderableFromLabel(labelForNested(element), new RegExp(camelCaseRegexp))
+ exactOrCamelMatches.push(element)
+ } else if(secondaryMatcher.test(labelForNested(element))){
+ element.renderable = renderableFromLabel(labelForNested(element), secondaryMatcher)
+ secondaryMatches.push(element)
+ }
+ })
+
+ return [...exactOrCamelMatches, ...secondaryMatches]
+ }
+
+ const modules = moduleSearchIndex ? processWithExactLabel(moduleSearchIndex, constants.labels.modules) : []
+ const packages = packageSearchIndex ? processPackages(packageSearchIndex) : []
+ const types = typeSearchIndex ? processNested(typeSearchIndex, constants.labels.types) : []
+ const members = memberSearchIndex ? processNested(memberSearchIndex, constants.labels.members) : []
+ const tags = tagSearchIndex ? processWithExactLabel(tagSearchIndex, constants.labels.tags) : []
+
+ const result = [...modules, ...packages, ...types, ...members, ...tags]
+ return response(result);
+ },
+ response: function(event, ui) {
+ if (!ui.content.length) {
+ ui.content.push(constants.noResult);
+ } else {
+ $("#search").empty();
+ }
+ },
+ autoFocus: true,
+ position: {
+ collision: "flip"
+ },
+ select: function(event, ui) {
+ if (ui.item.l !== constants.noResult.l) {
+ window.location.href = pathtoroot + ui.item.url;
+ $("#search").focus();
+ }
+ }
+ });
+});
+