summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorJohn Olheiser <42128690+jolheiser@users.noreply.github.com>2019-11-13 12:03:18 -0600
committerzeripath <art27@cantab.net>2019-11-13 18:03:18 +0000
commit3b0303a4fcbb8b8fda258e11edb3fd3c5c09396e (patch)
tree40bb579e03bc20b52fb2f6b82d078e325e0dd378 /docs
parentafe50873a5d6b52177b0cd6bc9d2657faf82f311 (diff)
downloadgitea-3b0303a4fcbb8b8fda258e11edb3fd3c5c09396e.tar.gz
gitea-3b0303a4fcbb8b8fda258e11edb3fd3c5c09396e.zip
Implement documentation search (#8937)
* Implement documentation search Signed-off-by: jolheiser <john.olheiser@gmail.com> Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/.gitignore1
-rw-r--r--docs/assets/js/search.js176
-rw-r--r--docs/config.yaml6
-rw-r--r--docs/content/doc/help.en-us.md4
-rw-r--r--docs/content/doc/help.fr-fr.md13
-rw-r--r--docs/content/doc/help.zh-cn.md4
-rw-r--r--docs/content/doc/help.zh-tw.md13
-rw-r--r--docs/content/doc/help/search.en-us.md25
-rw-r--r--docs/content/doc/help/search.fr-fr.md25
-rw-r--r--docs/content/doc/help/search.zh-cn.md25
-rw-r--r--docs/content/doc/help/search.zh-tw.md25
-rw-r--r--docs/layouts/_default/index.json5
-rw-r--r--docs/layouts/doc/search.html44
13 files changed, 362 insertions, 4 deletions
diff --git a/docs/.gitignore b/docs/.gitignore
index 55ec469a42..9cd1408bd2 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -1,3 +1,4 @@
public/
templates/swagger/v1_json.tmpl
themes/
+resources/
diff --git a/docs/assets/js/search.js b/docs/assets/js/search.js
new file mode 100644
index 0000000000..72d94c9ee2
--- /dev/null
+++ b/docs/assets/js/search.js
@@ -0,0 +1,176 @@
+function ready(fn) {
+ if (document.readyState != 'loading') {
+ fn();
+ } else {
+ document.addEventListener('DOMContentLoaded', fn);
+ }
+}
+
+ready(doSearch);
+
+const summaryInclude = 60;
+const fuseOptions = {
+ shouldSort: true,
+ includeMatches: true,
+ matchAllTokens: true,
+ threshold: 0.0, // for parsing diacritics
+ tokenize: true,
+ location: 0,
+ distance: 100,
+ maxPatternLength: 32,
+ minMatchCharLength: 1,
+ keys: [{
+ name: "title",
+ weight: 0.8
+ },
+ {
+ name: "contents",
+ weight: 0.5
+ },
+ {
+ name: "tags",
+ weight: 0.3
+ },
+ {
+ name: "categories",
+ weight: 0.3
+ }
+ ]
+};
+
+function param(name) {
+ return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
+}
+
+let searchQuery = param("s");
+
+function doSearch() {
+ if (searchQuery) {
+ document.getElementById("search-query").value = searchQuery;
+ executeSearch(searchQuery);
+ } else {
+ const para = document.createElement("P");
+ para.innerText = "Please enter a word or phrase above";
+ document.getElementById("search-results").appendChild(para);
+ }
+}
+
+function getJSON(url, fn) {
+ const request = new XMLHttpRequest();
+ request.open('GET', url, true);
+ request.onload = function () {
+ if (request.status >= 200 && request.status < 400) {
+ const data = JSON.parse(request.responseText);
+ fn(data);
+ } else {
+ console.log("Target reached on " + url + " with error " + request.status);
+ }
+ };
+ request.onerror = function () {
+ console.log("Connection error " + request.status);
+ };
+ request.send();
+}
+
+function executeSearch(searchQuery) {
+ getJSON("/" + document.LANG + "/index.json", function (data) {
+ const pages = data;
+ const fuse = new Fuse(pages, fuseOptions);
+ const result = fuse.search(searchQuery);
+ console.log({
+ "matches": result
+ });
+ document.getElementById("search-results").innerHTML = "";
+ if (result.length > 0) {
+ populateResults(result);
+ } else {
+ const para = document.createElement("P");
+ para.innerText = "No matches found";
+ document.getElementById("search-results").appendChild(para);
+ }
+ });
+}
+
+function populateResults(result) {
+ result.forEach(function (value, key) {
+ const content = value.item.contents;
+ let snippet = "";
+ const snippetHighlights = [];
+ if (fuseOptions.tokenize) {
+ snippetHighlights.push(searchQuery);
+ value.matches.forEach(function (mvalue) {
+ if (mvalue.key === "tags" || mvalue.key === "categories") {
+ snippetHighlights.push(mvalue.value);
+ } else if (mvalue.key === "contents") {
+ const ind = content.toLowerCase().indexOf(searchQuery.toLowerCase());
+ const start = ind - summaryInclude > 0 ? ind - summaryInclude : 0;
+ const end = ind + searchQuery.length + summaryInclude < content.length ? ind + searchQuery.length + summaryInclude : content.length;
+ snippet += content.substring(start, end);
+ if (ind > -1) {
+ snippetHighlights.push(content.substring(ind, ind + searchQuery.length))
+ } else {
+ snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0], mvalue.indices[0][1] - mvalue.indices[0][0] + 1));
+ }
+ }
+ });
+ }
+
+ if (snippet.length < 1) {
+ snippet += content.substring(0, summaryInclude * 2);
+ }
+ //pull template from hugo templarte definition
+ const templateDefinition = document.getElementById("search-result-template").innerHTML;
+ //replace values
+ const output = render(templateDefinition, {
+ key: key,
+ title: value.item.title,
+ link: value.item.permalink,
+ tags: value.item.tags,
+ categories: value.item.categories,
+ snippet: snippet
+ });
+ document.getElementById("search-results").appendChild(htmlToElement(output));
+
+ snippetHighlights.forEach(function (snipvalue) {
+ new Mark(document.getElementById("summary-" + key)).mark(snipvalue);
+ });
+
+ });
+}
+
+function render(templateString, data) {
+ let conditionalMatches, copy;
+ const conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
+ //since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
+ copy = templateString;
+ while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
+ if (data[conditionalMatches[1]]) {
+ //valid key, remove conditionals, leave content.
+ copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
+ } else {
+ //not valid, remove entire section
+ copy = copy.replace(conditionalMatches[0], '');
+ }
+ }
+ templateString = copy;
+ //now any conditionals removed we can do simple substitution
+ let key, find, re;
+ for (key in data) {
+ find = '\\$\\{\\s*' + key + '\\s*\\}';
+ re = new RegExp(find, 'g');
+ templateString = templateString.replace(re, data[key]);
+ }
+ return templateString;
+}
+
+/**
+ * By Mark Amery: https://stackoverflow.com/a/35385518
+ * @param {String} HTML representing a single element
+ * @return {Element}
+ */
+function htmlToElement(html) {
+ const template = document.createElement('template');
+ html = html.trim(); // Never return a text node of whitespace as the result
+ template.innerHTML = html;
+ return template.content.firstChild;
+}
diff --git a/docs/config.yaml b/docs/config.yaml
index 23d1257337..039e2938fb 100644
--- a/docs/config.yaml
+++ b/docs/config.yaml
@@ -20,6 +20,12 @@ params:
website: https://docs.gitea.io
version: 1.9.5
+outputs:
+ home:
+ - HTML
+ - RSS
+ - JSON
+
menu:
page:
- name: Website
diff --git a/docs/content/doc/help.en-us.md b/docs/content/doc/help.en-us.md
index 5ad1dd7f1e..635cb8931e 100644
--- a/docs/content/doc/help.en-us.md
+++ b/docs/content/doc/help.en-us.md
@@ -2,12 +2,12 @@
date: "2017-01-20T15:00:00+08:00"
title: "Help"
slug: "help"
-weight: 50
+weight: 5
toc: false
draft: false
menu:
sidebar:
name: "Help"
- weight: 50
+ weight: 5
identifier: "help"
---
diff --git a/docs/content/doc/help.fr-fr.md b/docs/content/doc/help.fr-fr.md
new file mode 100644
index 0000000000..ab0cedccfc
--- /dev/null
+++ b/docs/content/doc/help.fr-fr.md
@@ -0,0 +1,13 @@
+---
+date: "2017-01-20T15:00:00+08:00"
+title: "Aide"
+slug: "help"
+weight: 5
+toc: false
+draft: false
+menu:
+ sidebar:
+ name: "Aide"
+ weight: 5
+ identifier: "help"
+---
diff --git a/docs/content/doc/help.zh-cn.md b/docs/content/doc/help.zh-cn.md
index 6af7aa1719..9465cd5464 100644
--- a/docs/content/doc/help.zh-cn.md
+++ b/docs/content/doc/help.zh-cn.md
@@ -2,12 +2,12 @@
date: "2017-01-20T15:00:00+08:00"
title: "帮助"
slug: "help"
-weight: 50
+weight: 5
toc: false
draft: false
menu:
sidebar:
name: "帮助"
- weight: 50
+ weight: 5
identifier: "help"
---
diff --git a/docs/content/doc/help.zh-tw.md b/docs/content/doc/help.zh-tw.md
new file mode 100644
index 0000000000..c9cd794d81
--- /dev/null
+++ b/docs/content/doc/help.zh-tw.md
@@ -0,0 +1,13 @@
+---
+date: "2017-01-20T15:00:00+08:00"
+title: "救命"
+slug: "help"
+weight: 5
+toc: false
+draft: false
+menu:
+ sidebar:
+ name: "救命"
+ weight: 5
+ identifier: "help"
+---
diff --git a/docs/content/doc/help/search.en-us.md b/docs/content/doc/help/search.en-us.md
new file mode 100644
index 0000000000..93c154bde2
--- /dev/null
+++ b/docs/content/doc/help/search.en-us.md
@@ -0,0 +1,25 @@
+---
+date: "2019-11-12T16:00:00+02:00"
+title: "Search"
+slug: "search"
+weight: 4
+toc: true
+draft: false
+menu:
+ sidebar:
+ parent: "help"
+ name: "Search"
+ weight: 4
+ identifier: "search"
+sitemap:
+ priority : 0.1
+layout: "search"
+---
+
+
+This file exists solely to respond to /search URL with the related `search` layout template.
+
+No content shown here is rendered, all content is based in the template layouts/doc/search.html
+
+Setting a very low sitemap priority will tell search engines this is not important content.
+
diff --git a/docs/content/doc/help/search.fr-fr.md b/docs/content/doc/help/search.fr-fr.md
new file mode 100644
index 0000000000..3507e9efe8
--- /dev/null
+++ b/docs/content/doc/help/search.fr-fr.md
@@ -0,0 +1,25 @@
+---
+date: "2019-11-12T16:00:00+02:00"
+title: "Chercher"
+slug: "search"
+weight: 4
+toc: true
+draft: false
+menu:
+ sidebar:
+ parent: "help"
+ name: "Chercher"
+ weight: 4
+ identifier: "search"
+sitemap:
+ priority : 0.1
+layout: "search"
+---
+
+
+This file exists solely to respond to /search URL with the related `search` layout template.
+
+No content shown here is rendered, all content is based in the template layouts/doc/search.html
+
+Setting a very low sitemap priority will tell search engines this is not important content.
+
diff --git a/docs/content/doc/help/search.zh-cn.md b/docs/content/doc/help/search.zh-cn.md
new file mode 100644
index 0000000000..a51860aacd
--- /dev/null
+++ b/docs/content/doc/help/search.zh-cn.md
@@ -0,0 +1,25 @@
+---
+date: "2019-11-12T16:00:00+02:00"
+title: "搜索"
+slug: "search"
+weight: 4
+toc: true
+draft: false
+menu:
+ sidebar:
+ parent: "help"
+ name: "搜索"
+ weight: 4
+ identifier: "search"
+sitemap:
+ priority : 0.1
+layout: "search"
+---
+
+
+This file exists solely to respond to /search URL with the related `search` layout template.
+
+No content shown here is rendered, all content is based in the template layouts/doc/search.html
+
+Setting a very low sitemap priority will tell search engines this is not important content.
+
diff --git a/docs/content/doc/help/search.zh-tw.md b/docs/content/doc/help/search.zh-tw.md
new file mode 100644
index 0000000000..a51860aacd
--- /dev/null
+++ b/docs/content/doc/help/search.zh-tw.md
@@ -0,0 +1,25 @@
+---
+date: "2019-11-12T16:00:00+02:00"
+title: "搜索"
+slug: "search"
+weight: 4
+toc: true
+draft: false
+menu:
+ sidebar:
+ parent: "help"
+ name: "搜索"
+ weight: 4
+ identifier: "search"
+sitemap:
+ priority : 0.1
+layout: "search"
+---
+
+
+This file exists solely to respond to /search URL with the related `search` layout template.
+
+No content shown here is rendered, all content is based in the template layouts/doc/search.html
+
+Setting a very low sitemap priority will tell search engines this is not important content.
+
diff --git a/docs/layouts/_default/index.json b/docs/layouts/_default/index.json
new file mode 100644
index 0000000000..ae08324d8e
--- /dev/null
+++ b/docs/layouts/_default/index.json
@@ -0,0 +1,5 @@
+{{- $.Scratch.Add "index" slice -}}
+{{- range .Site.RegularPages -}}
+{{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
+{{- end -}}
+{{- $.Scratch.Get "index" | jsonify -}}
diff --git a/docs/layouts/doc/search.html b/docs/layouts/doc/search.html
new file mode 100644
index 0000000000..736fcaee10
--- /dev/null
+++ b/docs/layouts/doc/search.html
@@ -0,0 +1,44 @@
+{{ partial "header.html" . }}
+{{ partial "navbar.html" . }}
+
+<section class="section">
+ <div class="container is-centered page">
+ <div class="columns">
+ <div class="column is-one-quarter">
+ {{ partial "menu" . }}
+ </div>
+ <div class="column">
+ <div class=" content">
+ <section class="resume-section p-3 p-lg-5 d-flex flex-column">
+ <div class="my-auto" >
+ <form action="{{ "search" | absLangURL }}">
+ <label>Search:
+ <input id="search-query" name="s"/>
+ </label>
+ </form>
+ <br/>
+ <div id="search-results"></div>
+ </div>
+ </section>
+ <!-- this template is sucked in by search.js and appended to the search-results div above. So editing here will adjust style -->
+ <script id="search-result-template" type="text/x-js-template">
+ <div id="summary-${key}">
+ <h4><a href="${link}">${title}</a></h4>
+ <p>${snippet}</p>
+ ${ isset tags }<p>Tags: ${tags}</p>${ end }
+ ${ isset categories }<p>Categories: ${categories}</p>${ end }
+ <hr/>
+ </div>
+ </script>
+ </div>
+ </div>
+ </div>
+ </div>
+</section>
+
+<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.4.5/fuse.min.js"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"></script>
+<script>document.LANG = "{{ .Language.Lang }}";</script>
+{{ $script := resources.Get "js/search.js" | minify | fingerprint -}}
+<script src="{{ $script.Permalink }}" {{ printf "integrity=%q" $script.Data.Integrity | safeHTMLAttr }}></script>
+{{ partial "footer.html" . }}