diff --git a/CHANGELOG.md b/CHANGELOG.md index 74b30225..bac93c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.1.0 +### Features +- Add search plugin +```html + + +``` + ## 2.0.3 ### Bug fixes - fix: rendering emojis diff --git a/docs/README.md b/docs/README.md index 4968428a..53d2ecd0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -459,3 +459,39 @@ window.$docsify = { themeColor: '#3F51B5' } ``` + +## Plugins + +### Full Text Search + +If a document can have a search, can enhance some user experience. The introduction of search plugin is easy. such as + + +```html + + +``` + +By default, the hyperlink on the current page is recognized and the content is saved in `localStorage`. You can also specify the path to the files. + +!> Configure the content before the plugin is installed. + +```js +window.$docsify = { + search: 'auto', // default + + search : [ + '/', // => /README.md + '/guide', // => /guide.md + '/get-started', // => /get-started.md + '/zh-cn/', // => /zh-cn/README.md + ], + + // Full configuration + search: { + maxAge: 86400000, // Expiration time, the default one day + paths: [], // or 'auto' + placeholder: 'Type to search' + } +} +``` diff --git a/docs/zh-cn.md b/docs/zh-cn.md index 56f249bb..fc115305 100644 --- a/docs/zh-cn.md +++ b/docs/zh-cn.md @@ -466,3 +466,38 @@ window.$docsify = { themeColor: '#3F51B5' } ``` + +## Plugins + +### 全文检索 - search + +一份文档如果能有搜索功能会提升一些用户体验,加载搜索插件也很简单,直接引入就自动安装并启用。默认情况下会自动分析当前页面上的超链接,获取内容后建立索引并存储在 localStorage 里,默认过期时间为一天,当然这是可配置的。 + +自动识别的方式不一定能识别完整或者如果你想指定某些文件可索引,你可以自己指定文件的路径。 + +```html + + +``` + +!> 配置要在 docsify 引入之前 + +```js +window.$docsify = { + search: 'auto', // default + + search : [ + '/', // => /README.md + '/guide', // => /guide.md + '/get-started', // => /get-started.md + '/zh-cn/', // => /zh-cn/README.md + ], + + // Full configuration + search: { + maxAge: 86400000, // Expiration time, the default one day + paths: [], // or 'auto' + placeholder: 'Type to search' + } +} +``` diff --git a/src/event.js b/src/event.js index f9dca01c..64585bdc 100644 --- a/src/event.js +++ b/src/event.js @@ -126,7 +126,7 @@ export function bindToggle (dom) { dom.addEventListener('click', () => body.classList.toggle('close')) if (isMobile()) { - const sidebar = document.querySelector('.sidebar div') + const sidebar = document.querySelector('.sidebar') sidebar.addEventListener('click', () => { body.classList.toggle('close') setTimeout(() => activeLink(sidebar, true), 0) diff --git a/src/index.js b/src/index.js index ce0338aa..19a36517 100644 --- a/src/index.js +++ b/src/index.js @@ -58,6 +58,7 @@ const mainRender = function (cb) { } let page + if (!route) { page = OPTIONS.homepage || 'README.md' } else if (/\/$/.test(route)) { diff --git a/src/plugins/search.js b/src/plugins/search.js index 569bdd57..0bbf740e 100644 --- a/src/plugins/search.js +++ b/src/plugins/search.js @@ -50,14 +50,14 @@ const genFilePath = function (path) { filePath = basePath + filePath - return filePath.replace(/\/\//g, '/') + return filePath.replace(/\/+/g, '/') } /** * generate index */ const genIndex = function (path, content = '') { - // INDEXS[path] = {} + INDEXS[path] = { slug: '', title: '', body: '' } let slug content @@ -73,7 +73,7 @@ const genIndex = function (path, content = '') { //

const id = attr.match(/id="(\S+)"/)[1] - slug = `#/${path}#${id}`.replace(/\/\//, '/') + slug = `#/${path}#${id}`.replace(/\/+/, '/') INDEXS[slug] = { slug, title: text, body: '' } } else { // other html tag @@ -221,12 +221,10 @@ class SearchComponent { for (let i = 0; i < data.length; i++) { const post = data[i] let isMatch = false - let matchingNum = 0 let resultStr = '' const postTitle = post.title && post.title.trim() const postContent = post.body && post.body.trim() const postUrl = post.slug || '' - const postType = post.pagetitle if (postTitle !== '' && postContent !== '') { keywords.forEach((keyword, i) => { @@ -241,7 +239,6 @@ class SearchComponent { isMatch = false } else { isMatch = true - matchingNum++ if (indexContent < 0) indexContent = 0 let start = 0 @@ -266,9 +263,7 @@ class SearchComponent { const matchingPost = { title: escapeHtml(postTitle), content: resultStr, - url: postUrl, - type: postType, - matchingNum: matchingNum + url: postUrl } matchingResults.push(matchingPost) @@ -280,23 +275,30 @@ class SearchComponent { } } -// TODO 如果不存在就重新加载 const searchPlugin = function () { - if (localStorage.getItem('docsify.search.expires') > Date.now()) { - INDEXS = JSON.parse(localStorage.getItem('docsify.search.index')) + const isAuto = CONFIG.paths === 'auto' + const isExpired = localStorage.getItem('docsify.search.expires') < Date.now() + + INDEXS = JSON.parse(localStorage.getItem('docsify.search.index')) + + if (isExpired) { + INDEXS = {} + } else if (!isAuto) { return } - const paths = CONFIG.paths === 'auto' ? getAllPaths() : CONFIG.paths + let count = 0 + const paths = isAuto ? getAllPaths() : CONFIG.paths const len = paths.length const { load, marked, slugify } = window.Docsify.utils - let count = 0 const done = () => { localStorage.setItem('docsify.search.expires', Date.now() + CONFIG.maxAge) localStorage.setItem('docsify.search.index', JSON.stringify(INDEXS)) } paths.forEach(path => { + if (INDEXS[path]) return count++ + load(genFilePath(path)).then(content => { genIndex(path, marked(content)) slugify.clear() diff --git a/src/render.js b/src/render.js index f2335805..d8df5008 100644 --- a/src/render.js +++ b/src/render.js @@ -43,7 +43,7 @@ export function init () { } renderer.link = function (href, title, text) { if (!/:/.test(href)) { - href = `#/${href}`.replace(/\/\//g, '/') + href = `#/${href}`.replace(/\/+/g, '/') } return `${text}` diff --git a/src/util.js b/src/util.js index d5b62aea..7ea4f49d 100644 --- a/src/util.js +++ b/src/util.js @@ -89,7 +89,7 @@ export function getRoute () { const loc = window.location if (cacheHash === loc.hash && !isNil(cacheRoute)) return cacheRoute - let route = loc.hash.match(/^#\/([^#]+)/) + let route = loc.hash.replace(/%23/g, '#').match(/^#\/([^#]+)/) if (route && route.length === 2) { route = route[1]