From 90184ca3e7049188709bb804119833a6bfd263d4 Mon Sep 17 00:00:00 2001 From: Dillon Date: Wed, 15 Apr 2020 15:46:50 +0800 Subject: feat(search): add local search (#231) * feat(search): add local search * docs: add docs for search --- src/js/theme.js | 847 +++++++++++++++++++++++++++++----------------- src/lib/lunr.segmentit.js | 8 + 2 files changed, 545 insertions(+), 310 deletions(-) create mode 100644 src/lib/lunr.segmentit.js (limited to 'src') diff --git a/src/js/theme.js b/src/js/theme.js index f28d29f..4f17763 100644 --- a/src/js/theme.js +++ b/src/js/theme.js @@ -1,234 +1,445 @@ -(() => { - 'use strict'; - - class Util { - forEach(elements, handler) { - elements = elements || []; - for (let i = 0; i < elements.length; i++) { - handler(elements[i]); - } +class Util { + forEach(elements, handler) { + elements = elements || []; + for (let i = 0; i < elements.length; i++) { + handler(elements[i]); } + } - getScrollTop() { - return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; - } + getScrollTop() { + return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + } - isMobile() { - return window.matchMedia('only screen and (max-width: 680px)').matches; - } + isMobile() { + return window.matchMedia('only screen and (max-width: 680px)').matches; + } - isTocStatic() { - return window.matchMedia('only screen and (max-width: 960px)').matches; - } + isTocStatic() { + return window.matchMedia('only screen and (max-width: 960px)').matches; } +} - class Theme { - constructor() { - this.util = new Util(); - this.newScrollTop = this.util.getScrollTop(); - this.oldScrollTop = this.newScrollTop; - this.scrollEventSet = new Set(); - this.resizeEventSet = new Set(); - this.switchThemeEventSet = new Set(); +class Theme { + constructor() { + this.config = window.config; + this.contentData = this.config.content; + this.isDark = document.body.classList.contains('dark'); + this.util = new Util(); + this.newScrollTop = this.util.getScrollTop(); + this.oldScrollTop = this.newScrollTop; + this.scrollEventSet = new Set(); + this.resizeEventSet = new Set(); + this.switchThemeEventSet = new Set(); + this.clickMaskEventSet = new Set(); + } + + initMenuMobile() { + const $menuToggleMobile = document.getElementById('menu-toggle-mobile'); + const $menuMobile = document.getElementById('menu-mobile'); + $menuToggleMobile.addEventListener('click', () => { + document.body.classList.toggle('blur'); + $menuToggleMobile.classList.toggle('active'); + $menuMobile.classList.toggle('active'); + }, false); + this._menuMobileOnClickMask = this._menuMobileOnClickMask || (() => { + $menuToggleMobile.classList.remove('active'); + $menuMobile.classList.remove('active'); + }); + this.clickMaskEventSet.add(this._menuMobileOnClickMask); + } + + initSwitchTheme() { + this.util.forEach(document.getElementsByClassName('theme-switch'), $themeSwitch => { + $themeSwitch.addEventListener('click', () => { + document.body.classList.toggle('dark'); + this.isDark = !this.isDark; + window.localStorage && localStorage.setItem('theme', this.isDark ? 'dark' : 'light'); + for (let event of this.switchThemeEventSet) event(); + }, false); + }); + } + + initSearch() { + const searchConfig = this.config.search; + const isMobile = this.util.isMobile(); + if (!searchConfig || isMobile && this._searchMobileOnce || !isMobile && this._searchDesktopOnce) return; + const classSuffix = isMobile ? 'mobile' : 'desktop'; + const $header = document.getElementById(`header-${classSuffix}`); + const $searchInput = document.getElementById(`search-input-${classSuffix}`); + const $searchToggle = document.getElementById(`search-toggle-${classSuffix}`); + const $searchLoading = document.getElementById(`search-loading-${classSuffix}`); + const $searchClear = document.getElementById(`search-clear-${classSuffix}`); + if (isMobile) { + this._searchMobileOnce = true; + $searchInput.addEventListener('focus', () => { + document.body.classList.add('blur'); + $header.classList.add('open'); + }, false); + document.getElementById('search-cancel-mobile').addEventListener('click', () => { + $header.classList.remove('open'); + document.body.classList.remove('blur'); + document.getElementById('menu-toggle-mobile').classList.remove('active'); + document.getElementById('menu-mobile').classList.remove('active'); + $searchLoading.style.display = 'none'; + $searchClear.style.display = 'none'; + this._searchMobile && this._searchMobile.autocomplete.setVal(''); + }, false); + $searchClear.addEventListener('click', () => { + $searchClear.style.display = 'none'; + this._searchMobile && this._searchMobile.autocomplete.setVal(''); + }, false); + this._searchMobileOnClickMask = this._searchMobileOnClickMask || (() => { + $header.classList.remove('open'); + $searchLoading.style.display = 'none'; + $searchClear.style.display = 'none'; + this._searchMobile && this._searchMobile.autocomplete.setVal(''); + }); + this.clickMaskEventSet.add(this._searchMobileOnClickMask); + } else { + this._searchDesktopOnce = true; + $searchToggle.addEventListener('click', () => { + document.body.classList.add('blur'); + $header.classList.add('open'); + $searchInput.focus(); + }, false); + $searchClear.addEventListener('click', () => { + $searchClear.style.display = 'none'; + this._searchDesktop && this._searchDesktop.autocomplete.setVal(''); + }, false); + this._searchDesktopOnClickMask = this._searchDesktopOnClickMask || (() => { + $header.classList.remove('open'); + $searchLoading.style.display = 'none'; + $searchClear.style.display = 'none'; + this._searchDesktop && this._searchDesktop.autocomplete.setVal(''); + }); + this.clickMaskEventSet.add(this._searchDesktopOnClickMask); } + $searchInput.addEventListener('input', () => { + if ($searchInput.value === '') $searchClear.style.display = 'none'; + else $searchClear.style.display = 'inline'; + }, false); - initMenuMobile() { - const menuToggleMobile = document.getElementById('menu-toggle-mobile'); - const menuMobile = document.getElementById('menu-mobile'); - this._menuMobileOnScroll = this._menuMobileOnScroll || (() => { - menuToggleMobile.classList.remove('active'); - menuMobile.classList.remove('active'); + const CONTEXT_LENGTH = 200; + const initAutosearch = () => { + const autosearch = autocomplete(`#search-input-${classSuffix}`, { + hint: false, + autoselect: true, + dropdownMenuContainer: `#search-dropdown-${classSuffix}`, + clearOnSelected: true, + cssClasses: { noPrefix: true }, + // debug: true, + }, { + name: 'search', + source: (query, callback) => { + $searchLoading.style.display = 'inline'; + $searchClear.style.display = 'none'; + const finish = (results) => { + $searchLoading.style.display = 'none'; + $searchClear.style.display = 'inline'; + callback(results); + }; + if (searchConfig.type === 'lunr') { + const search = () => { + if (lunr.queryHandler) query = lunr.queryHandler(query); + return this._index.search(query).slice(0, 12).map(({ ref, matchData: { metadata } }) => { + const matchData = this._indexData[ref]; + let { title, content: context } = matchData; + let position = 0; + Object.values(metadata).forEach(({ description, content }) => { + if (description) { + context = matchData.description; + position = -1; + } else if (content) { + const matchPosition = content.position[0][0]; + if (matchPosition < position || position === 0) position = matchPosition; + } + }); + position -= CONTEXT_LENGTH / 5; + if (position > 0) { + position += context.substr(position, 25).lastIndexOf(' ') + 1; + context = '...' + context.substr(position, CONTEXT_LENGTH); + } else { + context = context.substr(0, CONTEXT_LENGTH); + } + Object.keys(metadata).forEach(key => { + title = title.replace(new RegExp(`(${key})`, 'gi'), '$1'); + context = context.replace(new RegExp(`(${key})`, 'gi'), '$1'); + }); + return { + 'uri': matchData.uri, + 'title' : title, + 'date' : matchData.date, + 'context' : context, + }; + }); + } + if (!this._index) { + fetch(searchConfig.lunrIndexURL) + .then(response => response.json()) + .then(data => { + const indexData = {}; + this._index = lunr(function () { + if (searchConfig.lunrLanguageCode) this.use(lunr[searchConfig.lunrLanguageCode]); + this.ref('uri'); + this.field('title', { boost: 50 }); + this.field('tags', { boost: 20 }); + this.field('description', { boost: 10 }); + this.field('content', { boost: 5 }); + this.metadataWhitelist = ['position']; + data.forEach((record) => { + indexData[record.uri] = record; + this.add(record); + }); + }); + this._indexData = indexData; + finish(search()); + }).catch(err => { + console.error(err); + finish([]); + }); + } else finish(search()); + } else if (searchConfig.type === 'algolia') { + $searchLoading.style.display = 'inline'; + $searchClear.style.display = 'none'; + this._algoliaIndex = this._algoliaIndex || algoliasearch(searchConfig.algoliaAppID, searchConfig.algoliaSearchKey).initIndex(searchConfig.algoliaIndex); + this._algoliaIndex + .search(query, { offset: 0, length: 12, attributesToHighlight: ['title', 'content'] }) + .then(({ hits }) => { + finish(hits.map(({ uri, date, _highlightResult: { title, content } }) => ({ + uri: uri, + title: title.value, + date: date, + context: content.value, + }))); + }) + .catch(err => { + console.error(err); + finish([]); + }); + } + }, + templates: { + suggestion: ({ title, date, context }) => `
${title}${date}
${context}
`, + empty: ({ query }) => `
${searchConfig.noResultsFound}: "${query}"
`, + footer: ({}) => { + const { searchType, icon, href } = searchConfig.type === 'algolia' ? { + searchType: 'algolia', + icon: '', + href: 'https://www.algolia.com/', + } : { + searchType: 'Lunr.js', + icon: '', + href: 'https://lunrjs.com/', + }; + return ``;}, + }, + }); + autosearch.on('autocomplete:selected', (event, suggestion, dataset, context) => { + window.location.assign(suggestion.uri); }); - if (this.util.isMobile()) { - menuToggleMobile.onclick = () => { - menuToggleMobile.classList.toggle('active'); - menuMobile.classList.toggle('active'); + if (isMobile) this._searchMobile = autosearch; + else this._searchDesktop = autosearch; + }; + if (searchConfig.lunrSegmentitURL && !document.getElementById('lunr-segmentit')) { + const script = document.createElement('script'); + script.id = 'lunr-segmentit'; + script.type = 'text/javascript'; + script.src = searchConfig.lunrSegmentitURL; + script.async = true; + if (script.readyState) { + script.onreadystatechange = () => { + if (script.readyState == 'loaded' || script.readyState == 'complete'){ + script.onreadystatechange = null; + initAutosearch(); + } }; - this.scrollEventSet.add(this._menuMobileOnScroll); } else { - this.scrollEventSet.delete(this._menuMobileOnScroll); + script.onload = () => { + initAutosearch(); + }; } - } + document.body.appendChild(script); + } else initAutosearch(); + } - initSwitchTheme() { - this.util.forEach(document.getElementsByClassName('theme-switch'), (button) => { - button.onclick = () => { - document.body.classList.toggle('dark-theme'); - window.isDark = !window.isDark; - window.localStorage && window.localStorage.setItem('theme', window.isDark ? 'dark' : 'light'); - for (let event of this.switchThemeEventSet) event(); - }; - }); - } + initLightGallery() { + if (this.config.lightGallery) lightGallery(document.getElementById('content'), this.config.lightGallery); + } - initHighlight() { - this.util.forEach(document.querySelectorAll('.highlight > .chroma'), (block) => { - const codes = block.querySelectorAll('pre.chroma > code'); - const code = codes[codes.length - 1]; - const lang = code ? code.className.toLowerCase() : ''; - block.className += ' ' + lang; - }); - this.util.forEach(document.querySelectorAll('.highlight > pre.chroma'), (block) => { - const chroma = document.createElement('div'); - chroma.className = block.className; - const table = document.createElement('table'); - chroma.appendChild(table); - const tbody = document.createElement('tbody'); - table.appendChild(tbody); - const tr = document.createElement('tr'); - tbody.appendChild(tr); - const td = document.createElement('td'); - tr.appendChild(td); - block.parentElement.replaceChild(chroma, block); - td.appendChild(block); - }); - } + initHighlight() { + this.util.forEach(document.querySelectorAll('.highlight > .chroma'), $chroma => { + const $codes = $chroma.querySelectorAll('pre.chroma > code'); + const $code = $codes[$codes.length - 1]; + const lang = $code ? $code.className.toLowerCase() : ''; + $chroma.className += ' ' + lang; + }); + this.util.forEach(document.querySelectorAll('.highlight > pre.chroma'), $preChroma => { + const $chroma = document.createElement('div'); + $chroma.className = $preChroma.className; + const $table = document.createElement('table'); + $chroma.appendChild($table); + const $tbody = document.createElement('tbody'); + $table.appendChild($tbody); + const $tr = document.createElement('tr'); + $tbody.appendChild($tr); + const $td = document.createElement('td'); + $tr.appendChild($td); + $preChroma.parentElement.replaceChild($chroma, $preChroma); + $td.appendChild($preChroma); + }); + } + + initTable() { + this.util.forEach(document.querySelectorAll('.content table'), $table => { + const $wrapper = document.createElement('div'); + $wrapper.className = 'table-wrapper'; + $table.parentElement.replaceChild($wrapper, $table); + $wrapper.appendChild($table); + }); + } - initTable() { - this.util.forEach(document.querySelectorAll('.content table'), (table) => { - const wrapper = document.createElement('div'); - wrapper.className = 'table-wrapper'; - table.parentElement.replaceChild(wrapper, table); - wrapper.appendChild(table); + initHeaderLink() { + for (let num = 1; num <= 6; num++) { + this.util.forEach(document.querySelectorAll('.page.single .content > h' + num), $header => { + $header.classList.add('headerLink'); + $header.innerHTML = `${$header.innerHTML}`; }); } + } - initHeaderLink() { - for (let num = 1; num <= 6; num++) { - this.util.forEach(document.querySelectorAll('.page.single .content > h' + num), (header) => { - header.classList.add('headerLink'); - header.innerHTML = `${header.innerHTML}`; - }); + initToc() { + const $tocCore = document.getElementById('TableOfContents'); + if ($tocCore === null) return; + if (this.util.isTocStatic()) { + const $tocContentStatic = document.getElementById('toc-content-static'); + if ($tocCore.parentElement !== $tocContentStatic) { + $tocCore.parentElement.removeChild($tocCore); + $tocContentStatic.appendChild($tocCore); } - } - - initToc() { - const tocCore = document.getElementById('TableOfContents'); - if (tocCore === null) return; - if (this.util.isTocStatic()) { - const tocContentStatic = document.getElementById('toc-content-static'); - if (tocCore.parentElement !== tocContentStatic) { - tocCore.parentElement.removeChild(tocCore); - tocContentStatic.appendChild(tocCore); - } - if (this._tocOnScroll) this.scrollEventSet.delete(this._tocOnScroll); - } else { - const tocContentAuto = document.getElementById('toc-content-auto'); - if (tocCore.parentElement !== tocContentAuto) { - tocCore.parentElement.removeChild(tocCore); - tocContentAuto.appendChild(tocCore); + if (this._tocOnScroll) this.scrollEventSet.delete(this._tocOnScroll); + } else { + const $tocContentAuto = document.getElementById('toc-content-auto'); + if ($tocCore.parentElement !== $tocContentAuto) { + $tocCore.parentElement.removeChild($tocCore); + $tocContentAuto.appendChild($tocCore); + } + const $toc = document.getElementById('toc-auto'); + const $page = document.getElementsByClassName('page')[0]; + const rect = $page.getBoundingClientRect(); + $toc.style.left = `${rect.left + rect.width + 20}px`; + $toc.style.maxWidth = `${$page.getBoundingClientRect().left - 20}px`; + const $tocLinkElements = $tocCore.getElementsByTagName('a'); + const $tocLiElements = $tocCore.getElementsByTagName('li'); + const $headerLinkElements = document.getElementsByClassName('headerLink'); + const headerIsFixed = this.config.desktopHeaderMode !== 'normal'; + const headerHeight = document.getElementById('header-desktop').offsetHeight; + const TOP_SPACING = 20 + (headerIsFixed ? headerHeight : 0); + const minTocTop = $toc.offsetTop; + const minScrollTop = minTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight); + this._tocOnScroll = this._tocOnScroll || (() => { + const footerTop = document.getElementById('post-footer').offsetTop; + const maxTocTop = footerTop - $toc.getBoundingClientRect().height; + const maxScrollTop = maxTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight); + if (this.newScrollTop < minScrollTop) { + $toc.style.position = 'absolute'; + $toc.style.top = `${minTocTop}px`; + } else if (this.newScrollTop > maxScrollTop) { + $toc.style.position = 'absolute'; + $toc.style.top = `${maxTocTop}px`; + } else { + $toc.style.position = 'fixed'; + $toc.style.top = `${TOP_SPACING}px`; } - const toc = document.getElementById('toc-auto'); - const page = document.getElementsByClassName('page')[0]; - toc.style.maxWidth = `${page.getBoundingClientRect().left - 20}px`; - this._tocLinks = this._tocLinks || tocCore.getElementsByTagName('a'); - this._tocLis = this._tocLis || tocCore.getElementsByTagName('li'); - this._headerLinks = this._headerLinks || document.getElementsByClassName('headerLink'); - const headerIsFixed = window.desktopHeaderMode !== 'normal'; - const headerHeight = document.getElementById('header-desktop').offsetHeight; - const TOP_SPACING = 20 + (headerIsFixed ? headerHeight : 0); - const minTocTop = toc.offsetTop; - const minScrollTop = minTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight); - this._tocOnScroll = this._tocOnScroll || (() => { - const footerTop = document.getElementById('post-footer').offsetTop; - const maxTocTop = footerTop - toc.getBoundingClientRect().height; - const maxScrollTop = maxTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight); - const rect = page.getBoundingClientRect(); - if (this.newScrollTop < minScrollTop) { - toc.style.position = 'absolute'; - toc.style.top = `${minTocTop}px`; - toc.style.left = `${rect.width + 20}px`; - } else if (this.newScrollTop > maxScrollTop) { - toc.style.position = 'absolute'; - toc.style.top = `${maxTocTop}px`; - toc.style.left = `${rect.width + 20}px`; - } else { - toc.style.position = 'fixed'; - toc.style.top = `${TOP_SPACING}px`; - toc.style.left = `${rect.left + rect.width + 20}px`; - } - this.util.forEach(this._tocLinks, (link) => { link.classList.remove('active'); }); - this.util.forEach(this._tocLis, (link) => { link.classList.remove('has-active'); }); - const INDEX_SPACING = 20 + (headerIsFixed ? headerHeight : 0); - let activeTocIndex = this._headerLinks.length - 1; - for (let i = 0; i < this._headerLinks.length - 1; i++) { - const thisTop = this._headerLinks[i].getBoundingClientRect().top; - const nextTop = this._headerLinks[i + 1].getBoundingClientRect().top; - if ((i == 0 && thisTop > INDEX_SPACING) - || (thisTop <= INDEX_SPACING && nextTop > INDEX_SPACING)) { - activeTocIndex = i; - break; - } + this.util.forEach($tocLinkElements, link => { link.classList.remove('active'); }); + this.util.forEach($tocLiElements, link => { link.classList.remove('has-active'); }); + const INDEX_SPACING = 20 + (headerIsFixed ? headerHeight : 0); + let activeTocIndex = $headerLinkElements.length - 1; + for (let i = 0; i < $headerLinkElements.length - 1; i++) { + const thisTop = $headerLinkElements[i].getBoundingClientRect().top; + const nextTop = $headerLinkElements[i + 1].getBoundingClientRect().top; + if ((i == 0 && thisTop > INDEX_SPACING) + || (thisTop <= INDEX_SPACING && nextTop > INDEX_SPACING)) { + activeTocIndex = i; + break; } - if (activeTocIndex !== -1) { - this._tocLinks[activeTocIndex].classList.add('active'); - let parent = this._tocLinks[activeTocIndex].parentElement; - while (parent !== tocCore) { - parent.classList.add('has-active'); - parent = parent.parentElement.parentElement; - } + } + if (activeTocIndex !== -1) { + $tocLinkElements[activeTocIndex].classList.add('active'); + let $parent = $tocLinkElements[activeTocIndex].parentElement; + while ($parent !== $tocCore) { + $parent.classList.add('has-active'); + $parent = $parent.parentElement.parentElement; } - }); - this._tocOnScroll(); - this.scrollEventSet.add(this._tocOnScroll); - } + } + }); + this._tocOnScroll(); + this.scrollEventSet.add(this._tocOnScroll); } + } - initMermaid() { - const elements = document.getElementsByClassName('mermaid'); - if (elements.length) { - mermaid.initialize({startOnLoad: false, theme: 'null'}); - this.util.forEach(elements, (element) => { - mermaid.mermaidAPI.render('svg-' + element.id, window.contentMap[element.id], (svgCode) => { - element.innerHTML = svgCode; - }, element); - }); - } - } + initMath() { + if (this.config.math) renderMathInElement(document.body, this.config.math); + } - initEcharts() { - this._echartsOnSwitchTheme = this._echartsOnSwitchTheme || (() => { - this._echartsArr = this._echartsArr || []; - for (let i = 0; i < this._echartsArr.length; i++) { - this._echartsArr[i].dispose(); - } - this._echartsArr = []; - this.util.forEach(document.getElementsByClassName('echarts'), (element) => { - const chart = echarts.init(element, window.isDark ? 'dark' : 'macarons', {renderer: 'svg'}); - chart.setOption(JSON.parse(window.contentMap[element.id])); - this._echartsArr.push(chart); - }); + initMermaid() { + const $mermaidElements = document.getElementsByClassName('mermaid'); + if ($mermaidElements.length) { + mermaid.initialize({startOnLoad: false, theme: 'null'}); + this.util.forEach($mermaidElements, element => { + mermaid.mermaidAPI.render('svg-' + element.id, this.contentData[element.id], svgCode => { + element.innerHTML = svgCode; + }, element); }); - this.switchThemeEventSet.add(this._echartsOnSwitchTheme); - this._echartsOnSwitchTheme(); - this._echartsOnResize = this._echartsOnResize || (() => { - for (let i = 0; i < this._echartsArr.length; i++) { - this._echartsArr[i].resize(); - } - }); - this.resizeEventSet.add(this._echartsOnResize); } + } - initMapbox() { + initEcharts() { + this._echartsOnSwitchTheme = this._echartsOnSwitchTheme || (() => { + this._echartsArr = this._echartsArr || []; + for (let i = 0; i < this._echartsArr.length; i++) { + this._echartsArr[i].dispose(); + } + this._echartsArr = []; + this.util.forEach(document.getElementsByClassName('echarts'), element => { + const chart = echarts.init(element, this.isDark ? 'dark' : 'macarons', {renderer: 'svg'}); + chart.setOption(JSON.parse(this.contentData[element.id])); + this._echartsArr.push(chart); + }); + }); + this.switchThemeEventSet.add(this._echartsOnSwitchTheme); + this._echartsOnSwitchTheme(); + this._echartsOnResize = this._echartsOnResize || (() => { + for (let i = 0; i < this._echartsArr.length; i++) { + this._echartsArr[i].resize(); + } + }); + this.resizeEventSet.add(this._echartsOnResize); + } + + initMapbox() { + if (this.config.mapbox) { + mapboxgl.accessToken = this.config.mapbox.accessToken; + mapboxgl.setRTLTextPlugin(this.config.mapbox.RTLTextPlugin); this._mapboxArr = this._mapboxArr || []; - this.util.forEach(document.getElementsByClassName('mapbox'), (element) => { - const options = window.contentMap[element.id]; + this.util.forEach(document.getElementsByClassName('mapbox'), element => { + const { lng, lat, zoom, lightStyle, darkStyle, marked, navigation, geolocate, scale, fullscreen } = this.contentData[element.id]; + const options = this.contentData[element.id]; const mapbox = new mapboxgl.Map({ container: element, - center: [options['lng'], options['lat']], - zoom: options['zoom'], + center: [lng, lat], + zoom: zoom, minZoom: .2, - style: window.isDark ? options['dark-style'] : options['light-style'], + style: this.isDark ? darkStyle : lightStyle, attributionControl: false, }); - if (options['marked']) { - new mapboxgl.Marker().setLngLat([options['lng'], options['lat']]).addTo(mapbox); + if (marked) { + new mapboxgl.Marker().setLngLat([lng, lat]).addTo(mapbox); } - if (options['navigation']) { + if (navigation) { mapbox.addControl(new mapboxgl.NavigationControl(), 'bottom-right'); } - if (options['geolocate']) { + if (geolocate) { mapbox.addControl(new mapboxgl.GeolocateControl({ positionOptions: { enableHighAccuracy: true, @@ -237,150 +448,166 @@ trackUserLocation: true, }), 'bottom-right'); } - if (options['scale']) { + if (scale) { mapbox.addControl(new mapboxgl.ScaleControl()); } - if (options['fullscreen']) { + if (fullscreen) { mapbox.addControl(new mapboxgl.FullscreenControl()); } mapbox.addControl(new MapboxLanguage()); this._mapboxArr.push(mapbox); }); this._mapboxOnSwitchTheme = this._mapboxOnSwitchTheme || (() => { - this.util.forEach(this._mapboxArr, (mapbox) => { + this.util.forEach(this._mapboxArr, mapbox => { const element = mapbox.getContainer(); - const options = window.contentMap[element.id]; - mapbox.setStyle(window.isDark ? options['dark-style'] : options['light-style']); + const { lightStyle, darkStyle } = this.contentData[element.id]; + mapbox.setStyle(this.isDark ? darkStyle : lightStyle); mapbox.addControl(new MapboxLanguage()); }); }); this.switchThemeEventSet.add(this._mapboxOnSwitchTheme); } + } - initTypeit() { - if (window.typeitArr) { - for (let i = 0; i < window.typeitArr.length; i++) { - const group = window.typeitArr[i]; - (function typeone(i) { - const id = group[i]; - if (i === group.length - 1) { - new TypeIt(`#${id}`, { - strings: window.contentMap[id], - }).go(); - return; - } - let instance = new TypeIt(`#${id}`, { - strings: window.contentMap[id], - afterComplete: () => { - instance.destroy(); - typeone(i + 1); - }, + initTypeit() { + if (this.config.typeit) { + const typeitData = this.config.typeit; + for (let i = 0; i < typeitData.length; i++) { + const group = typeitData[i]; + ((i) => { + const id = group[i]; + if (i === group.length - 1) { + new TypeIt(`#${id}`, { + strings: this.contentData[id], }).go(); - })(0); - } + return; + } + let instance = new TypeIt(`#${id}`, { + strings: this.contentData[id], + afterComplete: () => { + instance.destroy(); + typeone(i + 1); + }, + }).go(); + })(0); } } + } - initSmoothScroll() { - if ((!this.util.isMobile() && window.desktopHeaderMode === 'normal') - || (this.util.isMobile() && window.mobileHeaderMode === 'normal')) { - new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true}); - } else { - new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true, header: '#header-desktop'}); - } + initComment() { + if (this.config.comment && this.config.comment.gitalk) { + this.config.comment.gitalk.body = decodeURI(window.location.href); + const gitalk = new Gitalk(this.config.comment.gitalk.body); + gitalk.render('gitalk'); } + if (this.config.comment && this.config.comment.valine) new Valine(this.config.comment.valine); + } - onScroll() { - const headers = []; - if (window.desktopHeaderMode === 'auto') headers.push(document.getElementById('header-desktop')); - if (window.mobileHeaderMode === 'auto') headers.push(document.getElementById('header-mobile')); - this.util.forEach(headers, (header) => { - header.classList.add('animated'); - header.classList.add('faster'); - }); - const comments = document.getElementsByClassName('comment'); - if (comments.length) { - const button = document.getElementById('comment-button'); - button.href = `#${comments[0].id}`; - button.style.display = 'block'; - } - const fixedButtons = document.getElementById('fixed-buttons'); - const MIN_SCROLL = 10; - window.addEventListener('scroll', () => { - this.newScrollTop = this.util.getScrollTop(); - const scroll = this.newScrollTop - this.oldScrollTop; - this.util.forEach(headers, (header) => { - if (scroll > MIN_SCROLL) { - header.classList.remove('fadeInDown'); - header.classList.add('fadeOutUp'); - } else if (scroll < - MIN_SCROLL) { - header.classList.remove('fadeOutUp'); - header.classList.add('fadeInDown'); - } - }); - if (this.newScrollTop > 20) { - if (scroll > MIN_SCROLL) { - fixedButtons.classList.remove('fadeIn'); - fixedButtons.classList.add('fadeOut'); - } else if (scroll < - MIN_SCROLL) { - fixedButtons.style.display = 'block'; - fixedButtons.classList.remove('fadeOut'); - fixedButtons.classList.add('fadeIn'); - } - } else { - fixedButtons.style.display = 'none'; - } - if (!this._scrollTimeout) { - this._scrollTimeout = window.setTimeout(() => { - this._scrollTimeout = null; - for (let event of this.scrollEventSet) event(); - }, 10); - } - this.oldScrollTop = this.newScrollTop; - }, false); + initSmoothScroll() { + if ((!this.util.isMobile() && this.config.desktopHeaderMode === 'normal') + || (this.util.isMobile() && this.config.mobileHeaderMode === 'normal')) { + new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true}); + } else { + new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true, header: '#header-desktop'}); } + } - onResize() { - window.addEventListener('resize', () => { - if (!this._resizeTimeout) { - this._resizeTimeout = window.setTimeout(() => { - this._resizeTimeout = null; - for (let event of this.resizeEventSet) event(); - this.initMenuMobile(); - this.initToc(); - this.initSmoothScroll(); - this.initMermaid() - }, 100); - } - }, false); + onScroll() { + const $headers = []; + if (this.config.desktopHeaderMode === 'auto') $headers.push(document.getElementById('header-desktop')); + if (this.config.mobileHeaderMode === 'auto') $headers.push(document.getElementById('header-mobile')); + this.util.forEach($headers, $header => { + $header.classList.add('animated'); + $header.classList.add('faster'); + }); + if (document.getElementById('comments')) { + const $viewComments = document.getElementById('view-comments'); + $viewComments.href = `#comments`; + $viewComments.style.display = 'block'; } + const $fixedButtons = document.getElementById('fixed-buttons'); + const MIN_SCROLL = 20; + window.addEventListener('scroll', () => { + this.newScrollTop = this.util.getScrollTop(); + const scroll = this.newScrollTop - this.oldScrollTop; + this.util.forEach($headers, header => { + if (scroll > MIN_SCROLL) { + header.classList.remove('fadeInDown'); + header.classList.add('fadeOutUp'); + } else if (scroll < - MIN_SCROLL) { + header.classList.remove('fadeOutUp'); + header.classList.add('fadeInDown'); + } + }); + if (this.newScrollTop > MIN_SCROLL) { + if (scroll > MIN_SCROLL) { + $fixedButtons.classList.remove('fadeIn'); + $fixedButtons.classList.add('fadeOut'); + } else if (scroll < - MIN_SCROLL) { + $fixedButtons.style.display = 'block'; + $fixedButtons.classList.remove('fadeOut'); + $fixedButtons.classList.add('fadeIn'); + } + } else { + $fixedButtons.style.display = 'none'; + } + for (let event of this.scrollEventSet) event(); + this.oldScrollTop = this.newScrollTop; + }, false); + } - init() { - this.initMenuMobile(); - this.initSwitchTheme(); - this.initHighlight(); - this.initTable(); - this.initHeaderLink(); - this.initMermaid(); - this.initEcharts(); - this.initMapbox(); - this.initTypeit(); - this.initToc(); - this.initSmoothScroll(); + onResize() { + window.addEventListener('resize', () => { + if (!this._resizeTimeout) { + this._resizeTimeout = window.setTimeout(() => { + this._resizeTimeout = null; + for (let event of this.resizeEventSet) event(); + this.initToc(); + this.initSmoothScroll(); + this.initMermaid(); + this.initSearch(); + }, 100); + } + }, false); + } - this.onScroll(); - this.onResize(); - } + onClickMask() { + document.getElementById('mask').addEventListener('click', () => { + for (let event of this.clickMaskEventSet) event(); + document.body.classList.remove('blur'); + }, false); } - const themeInit = () => { - const theme = new Theme(); - theme.init(); - }; + init() { + this.initMenuMobile(); + this.initSwitchTheme(); + this.initSearch(); + this.initLightGallery(); + this.initHighlight(); + this.initTable(); + this.initHeaderLink(); + this.initMath(); + this.initMermaid(); + this.initEcharts(); + this.initMapbox(); + this.initTypeit(); + this.initToc(); + this.initComment(); + this.initSmoothScroll(); - if (document.readyState !== 'loading') { - themeInit(); - } else { - document.addEventListener('DOMContentLoaded', themeInit, false); + this.onScroll(); + this.onResize(); + this.onClickMask(); } -})(); +} + +const themeInit = () => { + const theme = new Theme(); + theme.init(); +}; + +if (document.readyState !== 'loading') { + themeInit(); +} else { + document.addEventListener('DOMContentLoaded', themeInit, false); +} diff --git a/src/lib/lunr.segmentit.js b/src/lib/lunr.segmentit.js new file mode 100644 index 0000000..570274c --- /dev/null +++ b/src/lib/lunr.segmentit.js @@ -0,0 +1,8 @@ +import { Segment, useDefault } from 'segmentit'; + +const segmentit = useDefault(new Segment()); +lunr.segmentit = segmentit; +lunr.queryHandler = query => { + if (/^[\u4e00-\u9fa5]+$/.test(query)) query = lunr.segmentit.doSegment(query).map(seg => '+' + seg.w).join(' '); + return query; +}; -- cgit v1.2.3