From eb6dd6f2eea1da37ca35a8374a06e53626a6a4c3 Mon Sep 17 00:00:00 2001 From: Peter Stuifzand Date: Sun, 17 May 2020 15:09:18 +0200 Subject: [PATCH] Add search to wiki --- editor/package-lock.json | 5 +++ editor/package.json | 2 + editor/src/index.js | 59 +++++++++++++++++++++------ editor/src/search.js | 26 ++++++++++++ file.go | 4 ++ main.go | 45 +++++++++++++++++++++ templates/editorjs.html | 1 - templates/layout.html | 87 ++++++++++++++++++++++++++++++++++------ 8 files changed, 202 insertions(+), 27 deletions(-) create mode 100644 editor/src/search.js diff --git a/editor/package-lock.json b/editor/package-lock.json index 23731ff..ef03a27 100644 --- a/editor/package-lock.json +++ b/editor/package-lock.json @@ -4220,6 +4220,11 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "mustache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.1.tgz", + "integrity": "sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA==" + }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", diff --git a/editor/package.json b/editor/package.json index e5f3eb2..afc84ad 100644 --- a/editor/package.json +++ b/editor/package.json @@ -11,6 +11,8 @@ "axios": "^0.19.0", "bulma": "^0.7.5", "css-loader": "^3.2.0", + "lunr": "^2.3.8", + "mustache": "^4.0.1", "node-sass": "^4.12.0", "sass-loader": "^7.3.1", "style-loader": "^1.0.0", diff --git a/editor/src/index.js b/editor/src/index.js index 79d9460..5518a2f 100644 --- a/editor/src/index.js +++ b/editor/src/index.js @@ -1,6 +1,9 @@ import listEditor from 'wiki-list-editor'; import axios from 'axios'; import qs from 'querystring' +import $ from 'jquery'; +import search from './search'; +import Mustache from 'mustache'; import './styles.scss'; @@ -28,7 +31,7 @@ function Indicator(element, timeout) { done() { timeoutId = setTimeout(() => { element.classList.add('hidden') - }, timeout*1000); + }, timeout * 1000); }, setText(text) { @@ -53,19 +56,49 @@ function addIndicator(editor, indicator) { } } - let holder = document.getElementById('editor'); -let editor = listEditor(holder, JSON.parse(holder.dataset.input)); +if (holder) { + let editor = listEditor(holder, JSON.parse(holder.dataset.input)); -editor.on('change', function () { - let element = document.getElementById('editor'); - let indicator = Indicator(document.getElementById('save-indicator'), 2); - let saveUrl = element.dataset.saveurl; - let page = element.dataset.page; + editor.on('change', function () { + let element = document.getElementById('editor'); + let indicator = Indicator(document.getElementById('save-indicator'), 2); + let saveUrl = element.dataset.saveurl; + let page = element.dataset.page; - indicator.setText('has changes...'); - addIndicator( - addSaver(editor, saveUrl, page, () => indicator.setText('saving...')), - indicator - ).save() + indicator.setText('has changes...'); + addIndicator( + addSaver(editor, saveUrl, page, () => indicator.setText('saving...')), + indicator + ).save() + }) +} + +let timeout = null; +let searchInput = document.getElementById('search-input'); +search(searchInput).then(searcher => { + searchInput.addEventListener('keyup', function (e) { + clearTimeout(timeout); + + if (e.key === 'Escape') { + $(searchInput).val(''); + $('#autocomplete').hide(); + return; + } + + timeout = setTimeout(function () { + let query = $(searchInput).val() + if (query === '') { + let autocomplete = document.getElementById('autocomplete'); + $(autocomplete).hide() + return + } + $('#autocomplete').show() + let result = searcher.idx.search(query) + var template = document.getElementById('result-template').innerHTML; + var rendered = Mustache.render(template, {results: result}, {}, ['[[', ']]']); + let autocomplete = document.getElementById('autocomplete'); + autocomplete.innerHTML = rendered; + }, 500) + }) }) diff --git a/editor/src/search.js b/editor/src/search.js new file mode 100644 index 0000000..1fc443a --- /dev/null +++ b/editor/src/search.js @@ -0,0 +1,26 @@ +import lunr from 'lunr'; +import $ from 'jquery'; + +function search(element) { + return new Promise(function (resolve, reject) { + $.get('/documents.json', function (documents) { + let idx = lunr(function () { + this.ref('url') + this.field('title') + this.field('body') + + let lunridx = this + $.each(documents, function (index, doc) { + lunridx.add(doc) + }) + }) + + resolve({ + element: element, + idx: idx + }) + }) + }) +} + +export default search; diff --git a/file.go b/file.go index 12a5077..179d9a5 100644 --- a/file.go +++ b/file.go @@ -28,6 +28,7 @@ func NewFilePages(dirname string) PagesRepository { func (fp *FilePages) Get(title string) Page { name := strings.Replace(title, " ", "_", -1) + title = strings.Replace(title, "_", " ", -1) refs, err := loadBackrefs(fp, name) if err != nil { refs = nil @@ -279,6 +280,9 @@ func (fp *FilePages) AllPages() ([]Page, error) { var pages []Page for _, file := range files { + if file.Name()[0] == '.' { + continue + } if file.Name() == "backrefs.json" { continue } diff --git a/main.go b/main.go index 84fd474..d5982c3 100644 --- a/main.go +++ b/main.go @@ -590,6 +590,51 @@ func main() { mp = NewFilePages("data") http.Handle("/auth/", &authHandler{}) + http.HandleFunc("/documents.json", func(w http.ResponseWriter, r *http.Request) { + type Document struct { + Title string `json:"title"` + Body string `json:"body"` + URL string `json:"url"` + } + + var results []Document + pages, err := mp.(*FilePages).AllPages() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + for _, page := range pages { + content := page.Content + + var listItems []struct { + Id int + Indented int + Text string + } + + err = json.NewDecoder(strings.NewReader(content)).Decode(&listItems) + if err == nil { + pageText := "" + for _, item := range listItems { + pageText += strings.Repeat(" ", item.Indented) + "* " + item.Text + "\n" + } + + content = pageText + } + + results = append(results, Document{ + Title: page.Title, + Body: content, + URL: page.Name, + }) + } + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(&results) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + }) http.HandleFunc("/fetchLink", func(w http.ResponseWriter, r *http.Request) { link := r.URL.Query().Get("url") u, err := url.Parse(link) diff --git a/templates/editorjs.html b/templates/editorjs.html index 39f1a01..50b8d20 100644 --- a/templates/editorjs.html +++ b/templates/editorjs.html @@ -1,2 +1 @@
- diff --git a/templates/layout.html b/templates/layout.html index 3636c87..674f974 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -5,44 +5,81 @@ - - + + {{ .Title }} - Wiki {{ block "content_head" . }} {{ end }}