Skip to content

Commit 3b0303a

Browse files
jolheiserguillep2k
authored andcommitted
Implement documentation search (#8937)
* Implement documentation search Signed-off-by: jolheiser <[email protected]> Co-Authored-By: guillep2k <[email protected]>
1 parent afe5087 commit 3b0303a

File tree

13 files changed

+362
-4
lines changed

13 files changed

+362
-4
lines changed

docs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
public/
22
templates/swagger/v1_json.tmpl
33
themes/
4+
resources/

docs/assets/js/search.js

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
function ready(fn) {
2+
if (document.readyState != 'loading') {
3+
fn();
4+
} else {
5+
document.addEventListener('DOMContentLoaded', fn);
6+
}
7+
}
8+
9+
ready(doSearch);
10+
11+
const summaryInclude = 60;
12+
const fuseOptions = {
13+
shouldSort: true,
14+
includeMatches: true,
15+
matchAllTokens: true,
16+
threshold: 0.0, // for parsing diacritics
17+
tokenize: true,
18+
location: 0,
19+
distance: 100,
20+
maxPatternLength: 32,
21+
minMatchCharLength: 1,
22+
keys: [{
23+
name: "title",
24+
weight: 0.8
25+
},
26+
{
27+
name: "contents",
28+
weight: 0.5
29+
},
30+
{
31+
name: "tags",
32+
weight: 0.3
33+
},
34+
{
35+
name: "categories",
36+
weight: 0.3
37+
}
38+
]
39+
};
40+
41+
function param(name) {
42+
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
43+
}
44+
45+
let searchQuery = param("s");
46+
47+
function doSearch() {
48+
if (searchQuery) {
49+
document.getElementById("search-query").value = searchQuery;
50+
executeSearch(searchQuery);
51+
} else {
52+
const para = document.createElement("P");
53+
para.innerText = "Please enter a word or phrase above";
54+
document.getElementById("search-results").appendChild(para);
55+
}
56+
}
57+
58+
function getJSON(url, fn) {
59+
const request = new XMLHttpRequest();
60+
request.open('GET', url, true);
61+
request.onload = function () {
62+
if (request.status >= 200 && request.status < 400) {
63+
const data = JSON.parse(request.responseText);
64+
fn(data);
65+
} else {
66+
console.log("Target reached on " + url + " with error " + request.status);
67+
}
68+
};
69+
request.onerror = function () {
70+
console.log("Connection error " + request.status);
71+
};
72+
request.send();
73+
}
74+
75+
function executeSearch(searchQuery) {
76+
getJSON("/" + document.LANG + "/index.json", function (data) {
77+
const pages = data;
78+
const fuse = new Fuse(pages, fuseOptions);
79+
const result = fuse.search(searchQuery);
80+
console.log({
81+
"matches": result
82+
});
83+
document.getElementById("search-results").innerHTML = "";
84+
if (result.length > 0) {
85+
populateResults(result);
86+
} else {
87+
const para = document.createElement("P");
88+
para.innerText = "No matches found";
89+
document.getElementById("search-results").appendChild(para);
90+
}
91+
});
92+
}
93+
94+
function populateResults(result) {
95+
result.forEach(function (value, key) {
96+
const content = value.item.contents;
97+
let snippet = "";
98+
const snippetHighlights = [];
99+
if (fuseOptions.tokenize) {
100+
snippetHighlights.push(searchQuery);
101+
value.matches.forEach(function (mvalue) {
102+
if (mvalue.key === "tags" || mvalue.key === "categories") {
103+
snippetHighlights.push(mvalue.value);
104+
} else if (mvalue.key === "contents") {
105+
const ind = content.toLowerCase().indexOf(searchQuery.toLowerCase());
106+
const start = ind - summaryInclude > 0 ? ind - summaryInclude : 0;
107+
const end = ind + searchQuery.length + summaryInclude < content.length ? ind + searchQuery.length + summaryInclude : content.length;
108+
snippet += content.substring(start, end);
109+
if (ind > -1) {
110+
snippetHighlights.push(content.substring(ind, ind + searchQuery.length))
111+
} else {
112+
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0], mvalue.indices[0][1] - mvalue.indices[0][0] + 1));
113+
}
114+
}
115+
});
116+
}
117+
118+
if (snippet.length < 1) {
119+
snippet += content.substring(0, summaryInclude * 2);
120+
}
121+
//pull template from hugo templarte definition
122+
const templateDefinition = document.getElementById("search-result-template").innerHTML;
123+
//replace values
124+
const output = render(templateDefinition, {
125+
key: key,
126+
title: value.item.title,
127+
link: value.item.permalink,
128+
tags: value.item.tags,
129+
categories: value.item.categories,
130+
snippet: snippet
131+
});
132+
document.getElementById("search-results").appendChild(htmlToElement(output));
133+
134+
snippetHighlights.forEach(function (snipvalue) {
135+
new Mark(document.getElementById("summary-" + key)).mark(snipvalue);
136+
});
137+
138+
});
139+
}
140+
141+
function render(templateString, data) {
142+
let conditionalMatches, copy;
143+
const conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
144+
//since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
145+
copy = templateString;
146+
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
147+
if (data[conditionalMatches[1]]) {
148+
//valid key, remove conditionals, leave content.
149+
copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
150+
} else {
151+
//not valid, remove entire section
152+
copy = copy.replace(conditionalMatches[0], '');
153+
}
154+
}
155+
templateString = copy;
156+
//now any conditionals removed we can do simple substitution
157+
let key, find, re;
158+
for (key in data) {
159+
find = '\\$\\{\\s*' + key + '\\s*\\}';
160+
re = new RegExp(find, 'g');
161+
templateString = templateString.replace(re, data[key]);
162+
}
163+
return templateString;
164+
}
165+
166+
/**
167+
* By Mark Amery: https://stackoverflow.com/a/35385518
168+
* @param {String} HTML representing a single element
169+
* @return {Element}
170+
*/
171+
function htmlToElement(html) {
172+
const template = document.createElement('template');
173+
html = html.trim(); // Never return a text node of whitespace as the result
174+
template.innerHTML = html;
175+
return template.content.firstChild;
176+
}

docs/config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ params:
2020
website: https://docs.gitea.io
2121
version: 1.9.5
2222

23+
outputs:
24+
home:
25+
- HTML
26+
- RSS
27+
- JSON
28+
2329
menu:
2430
page:
2531
- name: Website

docs/content/doc/help.en-us.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
date: "2017-01-20T15:00:00+08:00"
33
title: "Help"
44
slug: "help"
5-
weight: 50
5+
weight: 5
66
toc: false
77
draft: false
88
menu:
99
sidebar:
1010
name: "Help"
11-
weight: 50
11+
weight: 5
1212
identifier: "help"
1313
---

docs/content/doc/help.fr-fr.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
date: "2017-01-20T15:00:00+08:00"
3+
title: "Aide"
4+
slug: "help"
5+
weight: 5
6+
toc: false
7+
draft: false
8+
menu:
9+
sidebar:
10+
name: "Aide"
11+
weight: 5
12+
identifier: "help"
13+
---

docs/content/doc/help.zh-cn.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
date: "2017-01-20T15:00:00+08:00"
33
title: "帮助"
44
slug: "help"
5-
weight: 50
5+
weight: 5
66
toc: false
77
draft: false
88
menu:
99
sidebar:
1010
name: "帮助"
11-
weight: 50
11+
weight: 5
1212
identifier: "help"
1313
---

docs/content/doc/help.zh-tw.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
date: "2017-01-20T15:00:00+08:00"
3+
title: "救命"
4+
slug: "help"
5+
weight: 5
6+
toc: false
7+
draft: false
8+
menu:
9+
sidebar:
10+
name: "救命"
11+
weight: 5
12+
identifier: "help"
13+
---

docs/content/doc/help/search.en-us.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
date: "2019-11-12T16:00:00+02:00"
3+
title: "Search"
4+
slug: "search"
5+
weight: 4
6+
toc: true
7+
draft: false
8+
menu:
9+
sidebar:
10+
parent: "help"
11+
name: "Search"
12+
weight: 4
13+
identifier: "search"
14+
sitemap:
15+
priority : 0.1
16+
layout: "search"
17+
---
18+
19+
20+
This file exists solely to respond to /search URL with the related `search` layout template.
21+
22+
No content shown here is rendered, all content is based in the template layouts/doc/search.html
23+
24+
Setting a very low sitemap priority will tell search engines this is not important content.
25+

docs/content/doc/help/search.fr-fr.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
date: "2019-11-12T16:00:00+02:00"
3+
title: "Chercher"
4+
slug: "search"
5+
weight: 4
6+
toc: true
7+
draft: false
8+
menu:
9+
sidebar:
10+
parent: "help"
11+
name: "Chercher"
12+
weight: 4
13+
identifier: "search"
14+
sitemap:
15+
priority : 0.1
16+
layout: "search"
17+
---
18+
19+
20+
This file exists solely to respond to /search URL with the related `search` layout template.
21+
22+
No content shown here is rendered, all content is based in the template layouts/doc/search.html
23+
24+
Setting a very low sitemap priority will tell search engines this is not important content.
25+

docs/content/doc/help/search.zh-cn.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
date: "2019-11-12T16:00:00+02:00"
3+
title: "搜索"
4+
slug: "search"
5+
weight: 4
6+
toc: true
7+
draft: false
8+
menu:
9+
sidebar:
10+
parent: "help"
11+
name: "搜索"
12+
weight: 4
13+
identifier: "search"
14+
sitemap:
15+
priority : 0.1
16+
layout: "search"
17+
---
18+
19+
20+
This file exists solely to respond to /search URL with the related `search` layout template.
21+
22+
No content shown here is rendered, all content is based in the template layouts/doc/search.html
23+
24+
Setting a very low sitemap priority will tell search engines this is not important content.
25+

docs/content/doc/help/search.zh-tw.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
date: "2019-11-12T16:00:00+02:00"
3+
title: "搜索"
4+
slug: "search"
5+
weight: 4
6+
toc: true
7+
draft: false
8+
menu:
9+
sidebar:
10+
parent: "help"
11+
name: "搜索"
12+
weight: 4
13+
identifier: "search"
14+
sitemap:
15+
priority : 0.1
16+
layout: "search"
17+
---
18+
19+
20+
This file exists solely to respond to /search URL with the related `search` layout template.
21+
22+
No content shown here is rendered, all content is based in the template layouts/doc/search.html
23+
24+
Setting a very low sitemap priority will tell search engines this is not important content.
25+

docs/layouts/_default/index.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{{- $.Scratch.Add "index" slice -}}
2+
{{- range .Site.RegularPages -}}
3+
{{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
4+
{{- end -}}
5+
{{- $.Scratch.Get "index" | jsonify -}}

0 commit comments

Comments
 (0)