pref: optimize theme config
This commit is contained in:
parent
f23ac2d795
commit
73ee16edce
110
_config.yml
110
_config.yml
|
@ -1,10 +1,16 @@
|
|||
# theme basic info
|
||||
theme_info:
|
||||
name: ILS
|
||||
version: 1.0.2
|
||||
version: 1.1.0
|
||||
author: XPoet
|
||||
repository: https://github.com/XPoet/hexo-theme-ils
|
||||
|
||||
# favicon
|
||||
favicon: images/favicon.png
|
||||
|
||||
# avatar
|
||||
avatar: images/avatar.png
|
||||
|
||||
# navigation menu
|
||||
menu:
|
||||
Home: /
|
||||
|
@ -17,84 +23,78 @@ menu:
|
|||
rss:
|
||||
enable: true
|
||||
|
||||
# favicon
|
||||
favicon: images/favicon.png
|
||||
|
||||
# comment plugin
|
||||
comments:
|
||||
# Valine.
|
||||
# more info please open https://github.com/xCss/Valine
|
||||
# Valine https://github.com/xCss/Valine
|
||||
valine:
|
||||
enable: false # true or false
|
||||
appid: # your leancloud application appid
|
||||
appkey: # your leancloud application appkey
|
||||
meta: # type: Array, default: ['nick','mail','link']
|
||||
placeholder: Please share your thoughts with us in the comments below! #your placeholder
|
||||
enable: false
|
||||
appid: # your leancloud application appid
|
||||
appkey: # your leancloud application appkey
|
||||
meta: # comment input meta, type: Array, values: ['nick','mail','link']
|
||||
placeholder: # your placeholder
|
||||
|
||||
# Gitalk
|
||||
# For more information: https://gitalk.github.io, https://github.com/gitalk/gitalk
|
||||
# Gitalk https://github.com/gitalk/gitalk
|
||||
gitalk:
|
||||
enable: false
|
||||
github_id: # GitHub repo owner
|
||||
repository: # Repository name to store issues
|
||||
client_id: # GitHub Application Client ID
|
||||
client_secret: # GitHub Application Client Secret
|
||||
github_id: # GitHub repo owner
|
||||
repository: # Repository name to store issues
|
||||
client_id: # GitHub Application Client ID
|
||||
client_secret: # GitHub Application Client Secret
|
||||
distraction_free_mode: false # Facebook-like distraction free mode
|
||||
|
||||
# Show PV/UV of the website/page with busuanzi.
|
||||
# Get more information on http://ibruce.info/2015/04/04/busuanzi/
|
||||
busuanzi_count:
|
||||
# count values only if the other configs are false
|
||||
enable: false
|
||||
# custom uv span for the whole site
|
||||
site_uv: false
|
||||
# custom pv span for the whole site
|
||||
site_pv: false
|
||||
# custom pv span for one page only
|
||||
page_pv: false
|
||||
# website count
|
||||
website_count:
|
||||
# busuanzi http://ibruce.info/2015/04/04/busuanzi/
|
||||
busuanzi_count:
|
||||
enable: false
|
||||
site_uv: false
|
||||
site_pv: false
|
||||
page_pv: false
|
||||
|
||||
# Local Search
|
||||
# Dependencies: https://github.com/theme-next/hexo-generator-searchdb
|
||||
local_search:
|
||||
enable: false
|
||||
enable: true
|
||||
|
||||
# If auto, trigger search by changing input.
|
||||
# If manual, trigger search by pressing enter key or search button.
|
||||
trigger: auto
|
||||
# Show top n results per article, show all results by setting to -1
|
||||
top_n_per_article: 1
|
||||
trigger: auto # values: auto | manual
|
||||
|
||||
# Unescape html strings to the readable one.
|
||||
unescape: false
|
||||
|
||||
# Preload the search data when the page loads.
|
||||
preload: false
|
||||
|
||||
# Code Block
|
||||
codeblock:
|
||||
style: flat # Available values: default | flat | mac
|
||||
copy_button:
|
||||
enable: true
|
||||
|
||||
# Table of Contents in the Sidebar
|
||||
toc:
|
||||
# Code Copy
|
||||
code_copy:
|
||||
enable: true
|
||||
# Automatically add list number to toc.
|
||||
number: true
|
||||
# If true, all level of TOC in a post will be displayed, rather than the activated part of it.
|
||||
expand_all: true
|
||||
|
||||
# magic theme style
|
||||
magic:
|
||||
enable: true
|
||||
sidebar:
|
||||
enable: true
|
||||
fixed: true
|
||||
position: right
|
||||
style: flat # values: default | flat | mac
|
||||
|
||||
# sidebar tools
|
||||
side-tools:
|
||||
side_tools:
|
||||
enable: true
|
||||
position: right
|
||||
position: right # values: left | right
|
||||
|
||||
# back to top
|
||||
back2top:
|
||||
enable: true
|
||||
position: right
|
||||
position: right # values: left | right
|
||||
|
||||
# Table of Contents in the Sidebar
|
||||
toc:
|
||||
enable: true
|
||||
|
||||
# Automatically add list number to toc.
|
||||
number: true
|
||||
|
||||
# If true, all level of TOC in a post will be displayed, rather than the activated part of it.
|
||||
expand_all: true
|
||||
|
||||
# magic theme
|
||||
magic:
|
||||
enable: true
|
||||
sidebar:
|
||||
enable: false
|
||||
fixed: true
|
||||
position: right # values: left | right
|
|
@ -26,7 +26,7 @@
|
|||
</ul>
|
||||
</span>
|
||||
<% } %>
|
||||
<% if (theme.busuanzi_count.enable && theme.busuanzi_count.page_pv && is_post()) { %>
|
||||
<% if (theme.website_count.busuanzi_count.enable && theme.website_count.busuanzi_count.page_pv && is_post()) { %>
|
||||
<span class="article-pv article-meta-item">
|
||||
<i class="fa fa-eye"></i> <span id="busuanzi_value_page_pv"></span>
|
||||
</span>
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
<%- __('powered_by', '<a target="_blank" href="https://hexo.io">Hexo</a>') %> | <%- __('theme') %> <a
|
||||
href="https://github.com/XPoet/hexo-theme-ils" target="_blank"><%= theme.theme_info.name %> v<%= theme.theme_info.version %></a>
|
||||
</div>
|
||||
<% if (theme.busuanzi_count.enable) { %>
|
||||
<% if (theme.website_count.busuanzi_count.enable) { %>
|
||||
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
|
||||
<div class="website-count info-item">
|
||||
<% if (theme.busuanzi_count.site_uv) { %>
|
||||
<% if (theme.website_count.busuanzi_count.site_uv) { %>
|
||||
<span id="busuanzi_container_site_uv" style="display: none">
|
||||
<%- __('site_uv') %><span id="busuanzi_value_site_uv"></span>
|
||||
</span>
|
||||
<% } %>
|
||||
<% if (theme.busuanzi_count.site_pv) { %>
|
||||
<% if (theme.website_count.busuanzi_count.site_pv) { %>
|
||||
<span id="busuanzi_container_site_pv" style="display: none">
|
||||
<%- __('site_pv') %><span id="busuanzi_value_site_pv"></span>
|
||||
</span>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<% if (is_post()) { %>
|
||||
|
||||
<% if (theme.codeblock.copy_button.enable) { %>
|
||||
<% if (theme.code_copy.enable) { %>
|
||||
<%- js('js/code-copy.js') %>
|
||||
<% } %>
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ if ((hexo-config('magic.enable') && !hexo-config('magic.sidebar.enable') && hexo
|
|||
position: fixed;
|
||||
top: $header-height + $component-interspace;
|
||||
|
||||
if (hexo-config('side-tools.position') == left) {
|
||||
if (hexo-config('side_tools.position') == left) {
|
||||
right: 30px;
|
||||
} else {
|
||||
left: 30px;
|
||||
|
|
|
@ -28,13 +28,13 @@ disable-user-select() {
|
|||
disable-user-select();
|
||||
the-transition();
|
||||
|
||||
if (hexo-config('codeblock.style') == 'flat') {
|
||||
if (hexo-config('code_copy.style') == 'flat') {
|
||||
background: white;
|
||||
border: 0;
|
||||
font-size: 0.8em;
|
||||
right: 0;
|
||||
top: 0;
|
||||
} else if (hexo-config('codeblock.style') == 'mac') {
|
||||
} else if (hexo-config('code_copy.style') == 'mac') {
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
right: 2px;
|
||||
|
@ -51,7 +51,7 @@ disable-user-select() {
|
|||
}
|
||||
|
||||
|
||||
if (hexo-config('codeblock.style') == 'mac') {
|
||||
if (hexo-config('code_copy.style') == 'mac') {
|
||||
.highlight-container {
|
||||
background: #21252b;
|
||||
border-radius: 5px;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// ==================================
|
||||
// THEME VARIABLE
|
||||
// ==================================
|
||||
// ===================================
|
||||
// THEME VARIABLE //
|
||||
// ===================================
|
||||
|
||||
// ===============================
|
||||
// ===================================
|
||||
// layout
|
||||
// ===============================
|
||||
// ===================================
|
||||
if (hexo-config('magic.enable')) {
|
||||
$header-height = 80px; // 头部默认高度
|
||||
} else {
|
||||
|
@ -22,15 +22,15 @@ $header-shrink-height = 60px; // 头部收缩高度
|
|||
$tool-button-width = 36px; // 方形工具按钮宽度
|
||||
$component-interspace = 30px; // 组件/模块的间隔值(px)
|
||||
|
||||
// ===============================
|
||||
// ===================================
|
||||
// 媒体查询
|
||||
// ===============================
|
||||
// ===================================
|
||||
$media-max-width = 760px; // 媒体查询最大宽度
|
||||
|
||||
|
||||
// ===============================
|
||||
// ===================================
|
||||
// z-index
|
||||
// ===============================
|
||||
// ===================================
|
||||
$z-index-1 = 1001;
|
||||
$z-index-2 = 1002;
|
||||
$z-index-3 = 1003;
|
||||
|
@ -39,16 +39,16 @@ $z-index-5 = 1005;
|
|||
$z-index-6 = 1006;
|
||||
|
||||
|
||||
// ===============================
|
||||
// ===================================
|
||||
// color
|
||||
// ===============================
|
||||
// ===================================
|
||||
// normal mode color
|
||||
$primary-color = #1b65ea; // 主题颜色
|
||||
$primary-color = #225eea; // 主题颜色
|
||||
$background-color = #fff; // 背景颜色
|
||||
$magic-background-color = #f5f5f5; // MAGIC 主题背景颜色
|
||||
$normal-text-color = #3c3944; // 通用文本颜色(默认字体颜色)
|
||||
$first-text-color = darken($normal-text-color, 10%); // 第一文本颜色
|
||||
$second-text-color = darken($normal-text-color, 5%); // 第二文本颜色
|
||||
$normal-text-color = #43404d; // 通用文本颜色(默认字体颜色)
|
||||
$first-text-color = darken($normal-text-color, 8%); // 第一文本颜色
|
||||
$second-text-color = darken($normal-text-color, 4%); // 第二文本颜色
|
||||
$third-text-color = lighten($normal-text-color, 40%); // 第三文本颜色
|
||||
$fourth-text-color = #eee; // 第四文本颜色
|
||||
$border-color = darken($background-color, 30%); // 边框颜色
|
||||
|
@ -67,9 +67,9 @@ $dark-border-color = lighten($dark-background-color, 20%);
|
|||
$dark-selection-color = $selection-color;
|
||||
|
||||
|
||||
// ===============================
|
||||
// ===================================
|
||||
// font
|
||||
// ===============================
|
||||
// ===================================
|
||||
//$default-font-family = 'PingHei', 'PingFang SC', 'Microsoft YaHei';
|
||||
//$default-font-family = 'PingFang SC', 'Hiragino Sans GB', 'STHeiti Light';
|
||||
//$default-font-family = 'Microsoft YaHei', 'SimHei', 'WenQuanYi Micro Hei', sans-serif;
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
.sidebar-tools {
|
||||
position: fixed;
|
||||
top: $header-height + $component-interspace;
|
||||
if (hexo-config('side-tools.position') == left) {
|
||||
if (hexo-config('side_tools.position') == left) {
|
||||
left: ((100% - $main-content-width) / 4);
|
||||
} else {
|
||||
right: ((100% - $main-content-width) / 4);
|
||||
|
@ -140,7 +140,7 @@
|
|||
}
|
||||
|
||||
.sidebar-tools {
|
||||
if (hexo-config('side-tools.position') == left) {
|
||||
if (hexo-config('side_tools.position') == left) {
|
||||
left: 2% !important;
|
||||
transform-origin: left top;
|
||||
} else {
|
||||
|
|
|
@ -1,297 +1,297 @@
|
|||
/* global CONFIG */
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
// Popup Window
|
||||
let isfetched = false;
|
||||
let datas;
|
||||
let isXml = true;
|
||||
// Search DB path
|
||||
let searchPath = CONFIG.path;
|
||||
if (searchPath.length === 0) {
|
||||
searchPath = 'search.xml';
|
||||
} else if (searchPath.endsWith('json')) {
|
||||
isXml = false;
|
||||
}
|
||||
const input = document.querySelector('.search-input');
|
||||
const resultContent = document.getElementById('search-result');
|
||||
|
||||
// Ref: https://github.com/ForbesLindesay/unescape-html
|
||||
const unescapeHtml = html => {
|
||||
return String(html)
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, '\'')
|
||||
.replace(/:/g, ':')
|
||||
// Replace all the other &#x; chars
|
||||
.replace(/&#(\d+);/g, (m, p) => {
|
||||
return String.fromCharCode(p);
|
||||
})
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&');
|
||||
};
|
||||
|
||||
const getIndexByWord = (word, text, caseSensitive) => {
|
||||
let wordLen = word.length;
|
||||
if (wordLen === 0) return [];
|
||||
let startPosition = 0;
|
||||
let position = [];
|
||||
let index = [];
|
||||
if (!caseSensitive) {
|
||||
text = text.toLowerCase();
|
||||
word = word.toLowerCase();
|
||||
// Popup Window
|
||||
let isfetched = false;
|
||||
let datas;
|
||||
let isXml = true;
|
||||
// Search DB path
|
||||
let searchPath = CONFIG.path;
|
||||
if (searchPath.length === 0) {
|
||||
searchPath = 'search.xml';
|
||||
} else if (searchPath.endsWith('json')) {
|
||||
isXml = false;
|
||||
}
|
||||
while ((position = text.indexOf(word, startPosition)) > -1) {
|
||||
index.push({ position, word });
|
||||
startPosition = position + wordLen;
|
||||
}
|
||||
return index;
|
||||
};
|
||||
const input = document.querySelector('.search-input');
|
||||
const resultContent = document.getElementById('search-result');
|
||||
|
||||
// Merge hits into slices
|
||||
const mergeIntoSlice = (start, end, index, searchText) => {
|
||||
let item = index[index.length - 1];
|
||||
let { position, word } = item;
|
||||
let hits = [];
|
||||
let searchTextCountInSlice = 0;
|
||||
while (position + word.length <= end && index.length !== 0) {
|
||||
if (word === searchText) {
|
||||
searchTextCountInSlice++;
|
||||
}
|
||||
hits.push({
|
||||
position,
|
||||
length: word.length
|
||||
});
|
||||
let wordEnd = position + word.length;
|
||||
|
||||
// Move to next position of hit
|
||||
index.pop();
|
||||
while (index.length !== 0) {
|
||||
item = index[index.length - 1];
|
||||
position = item.position;
|
||||
word = item.word;
|
||||
if (wordEnd > position) {
|
||||
index.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
hits,
|
||||
start,
|
||||
end,
|
||||
searchTextCount: searchTextCountInSlice
|
||||
// Ref: https://github.com/ForbesLindesay/unescape-html
|
||||
const unescapeHtml = html => {
|
||||
return String(html)
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, '\'')
|
||||
.replace(/:/g, ':')
|
||||
// Replace all the other &#x; chars
|
||||
.replace(/&#(\d+);/g, (m, p) => {
|
||||
return String.fromCharCode(p);
|
||||
})
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&');
|
||||
};
|
||||
};
|
||||
|
||||
// Highlight title and content
|
||||
const highlightKeyword = (text, slice) => {
|
||||
let result = '';
|
||||
let prevEnd = slice.start;
|
||||
slice.hits.forEach(hit => {
|
||||
result += text.substring(prevEnd, hit.position);
|
||||
let end = hit.position + hit.length;
|
||||
result += `<b class="search-keyword">${text.substring(hit.position, end)}</b>`;
|
||||
prevEnd = end;
|
||||
});
|
||||
result += text.substring(prevEnd, slice.end);
|
||||
return result;
|
||||
};
|
||||
const getIndexByWord = (word, text, caseSensitive) => {
|
||||
let wordLen = word.length;
|
||||
if (wordLen === 0) return [];
|
||||
let startPosition = 0;
|
||||
let position = [];
|
||||
let index = [];
|
||||
if (!caseSensitive) {
|
||||
text = text.toLowerCase();
|
||||
word = word.toLowerCase();
|
||||
}
|
||||
while ((position = text.indexOf(word, startPosition)) > -1) {
|
||||
index.push({position, word});
|
||||
startPosition = position + wordLen;
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
const inputEventFunction = () => {
|
||||
if (!isfetched) return;
|
||||
let searchText = input.value.trim().toLowerCase();
|
||||
let keywords = searchText.split(/[-\s]+/);
|
||||
if (keywords.length > 1) {
|
||||
keywords.push(searchText);
|
||||
}
|
||||
let resultItems = [];
|
||||
if (searchText.length > 0) {
|
||||
// Perform local searching
|
||||
datas.forEach(({ title, content, url }) => {
|
||||
let titleInLowerCase = title.toLowerCase();
|
||||
let contentInLowerCase = content.toLowerCase();
|
||||
let indexOfTitle = [];
|
||||
let indexOfContent = [];
|
||||
let searchTextCount = 0;
|
||||
keywords.forEach(keyword => {
|
||||
indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
|
||||
indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
|
||||
});
|
||||
|
||||
// Show search results
|
||||
if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
|
||||
let hitCount = indexOfTitle.length + indexOfContent.length;
|
||||
// Sort index by position of keyword
|
||||
[indexOfTitle, indexOfContent].forEach(index => {
|
||||
index.sort((itemLeft, itemRight) => {
|
||||
if (itemRight.position !== itemLeft.position) {
|
||||
return itemRight.position - itemLeft.position;
|
||||
}
|
||||
return itemLeft.word.length - itemRight.word.length;
|
||||
// Merge hits into slices
|
||||
const mergeIntoSlice = (start, end, index, searchText) => {
|
||||
let item = index[index.length - 1];
|
||||
let {position, word} = item;
|
||||
let hits = [];
|
||||
let searchTextCountInSlice = 0;
|
||||
while (position + word.length <= end && index.length !== 0) {
|
||||
if (word === searchText) {
|
||||
searchTextCountInSlice++;
|
||||
}
|
||||
hits.push({
|
||||
position,
|
||||
length: word.length
|
||||
});
|
||||
});
|
||||
let wordEnd = position + word.length;
|
||||
|
||||
let slicesOfTitle = [];
|
||||
if (indexOfTitle.length !== 0) {
|
||||
let tmp = mergeIntoSlice(0, title.length, indexOfTitle, searchText);
|
||||
searchTextCount += tmp.searchTextCountInSlice;
|
||||
slicesOfTitle.push(tmp);
|
||||
}
|
||||
|
||||
let slicesOfContent = [];
|
||||
while (indexOfContent.length !== 0) {
|
||||
let item = indexOfContent[indexOfContent.length - 1];
|
||||
let { position, word } = item;
|
||||
// Cut out 100 characters
|
||||
let start = position - 20;
|
||||
let end = position + 80;
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
// Move to next position of hit
|
||||
index.pop();
|
||||
while (index.length !== 0) {
|
||||
item = index[index.length - 1];
|
||||
position = item.position;
|
||||
word = item.word;
|
||||
if (wordEnd > position) {
|
||||
index.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (end < position + word.length) {
|
||||
end = position + word.length;
|
||||
}
|
||||
if (end > content.length) {
|
||||
end = content.length;
|
||||
}
|
||||
let tmp = mergeIntoSlice(start, end, indexOfContent, searchText);
|
||||
searchTextCount += tmp.searchTextCountInSlice;
|
||||
slicesOfContent.push(tmp);
|
||||
}
|
||||
|
||||
// Sort slices in content by search text's count and hits' count
|
||||
slicesOfContent.sort((sliceLeft, sliceRight) => {
|
||||
if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
|
||||
return sliceRight.searchTextCount - sliceLeft.searchTextCount;
|
||||
} else if (sliceLeft.hits.length !== sliceRight.hits.length) {
|
||||
return sliceRight.hits.length - sliceLeft.hits.length;
|
||||
}
|
||||
return sliceLeft.start - sliceRight.start;
|
||||
});
|
||||
|
||||
// Select top N slices in content
|
||||
let upperBound = parseInt(CONFIG.localsearch.top_n_per_article, 10);
|
||||
if (upperBound >= 0) {
|
||||
slicesOfContent = slicesOfContent.slice(0, upperBound);
|
||||
}
|
||||
|
||||
let resultItem = '';
|
||||
|
||||
if (slicesOfTitle.length !== 0) {
|
||||
resultItem += `<li><a href="${url}" class="search-result-title">${highlightKeyword(title, slicesOfTitle[0])}</a>`;
|
||||
} else {
|
||||
resultItem += `<li><a href="${url}" class="search-result-title">${title}</a>`;
|
||||
}
|
||||
|
||||
slicesOfContent.forEach(slice => {
|
||||
resultItem += `<a href="${url}"><p class="search-result">${highlightKeyword(content, slice)}...</p></a>`;
|
||||
});
|
||||
|
||||
resultItem += '</li>';
|
||||
resultItems.push({
|
||||
item: resultItem,
|
||||
id : resultItems.length,
|
||||
hitCount,
|
||||
searchTextCount
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (keywords.length === 1 && keywords[0] === '') {
|
||||
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x"></i></div>';
|
||||
} else if (resultItems.length === 0) {
|
||||
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x"></i></div>';
|
||||
} else {
|
||||
resultItems.sort((resultLeft, resultRight) => {
|
||||
if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
|
||||
return resultRight.searchTextCount - resultLeft.searchTextCount;
|
||||
} else if (resultLeft.hitCount !== resultRight.hitCount) {
|
||||
return resultRight.hitCount - resultLeft.hitCount;
|
||||
}
|
||||
return resultRight.id - resultLeft.id;
|
||||
});
|
||||
let searchResultList = '<ul class="search-result-list">';
|
||||
resultItems.forEach(result => {
|
||||
searchResultList += result.item;
|
||||
});
|
||||
searchResultList += '</ul>';
|
||||
resultContent.innerHTML = searchResultList;
|
||||
window.pjax && window.pjax.refresh(resultContent);
|
||||
}
|
||||
};
|
||||
return {
|
||||
hits,
|
||||
start,
|
||||
end,
|
||||
searchTextCount: searchTextCountInSlice
|
||||
};
|
||||
};
|
||||
|
||||
const fetchData = () => {
|
||||
fetch(CONFIG.root + searchPath)
|
||||
.then(response => response.text())
|
||||
.then(res => {
|
||||
// Get the contents from search data
|
||||
isfetched = true;
|
||||
datas = isXml ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => {
|
||||
return {
|
||||
title : element.querySelector('title').textContent,
|
||||
content: element.querySelector('content').textContent,
|
||||
url : element.querySelector('url').textContent
|
||||
};
|
||||
}) : JSON.parse(res);
|
||||
// Only match articles with not empty titles
|
||||
datas = datas.filter(data => data.title).map(data => {
|
||||
data.title = data.title.trim();
|
||||
data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : '';
|
||||
if (CONFIG.localsearch.unescape) {
|
||||
data.content = unescapeHtml(data.content);
|
||||
}
|
||||
data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/');
|
||||
return data;
|
||||
// Highlight title and content
|
||||
const highlightKeyword = (text, slice) => {
|
||||
let result = '';
|
||||
let prevEnd = slice.start;
|
||||
slice.hits.forEach(hit => {
|
||||
result += text.substring(prevEnd, hit.position);
|
||||
let end = hit.position + hit.length;
|
||||
result += `<b class="search-keyword">${text.substring(hit.position, end)}</b>`;
|
||||
prevEnd = end;
|
||||
});
|
||||
// Remove loading animation
|
||||
document.getElementById('no-result').innerHTML = '<i class="fa fa-search fa-5x"></i>';
|
||||
});
|
||||
};
|
||||
result += text.substring(prevEnd, slice.end);
|
||||
return result;
|
||||
};
|
||||
|
||||
if (CONFIG.localsearch.preload) {
|
||||
fetchData();
|
||||
}
|
||||
const inputEventFunction = () => {
|
||||
if (!isfetched) return;
|
||||
let searchText = input.value.trim().toLowerCase();
|
||||
let keywords = searchText.split(/[-\s]+/);
|
||||
if (keywords.length > 1) {
|
||||
keywords.push(searchText);
|
||||
}
|
||||
let resultItems = [];
|
||||
if (searchText.length > 0) {
|
||||
// Perform local searching
|
||||
datas.forEach(({title, content, url}) => {
|
||||
let titleInLowerCase = title.toLowerCase();
|
||||
let contentInLowerCase = content.toLowerCase();
|
||||
let indexOfTitle = [];
|
||||
let indexOfContent = [];
|
||||
let searchTextCount = 0;
|
||||
keywords.forEach(keyword => {
|
||||
indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
|
||||
indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
|
||||
});
|
||||
|
||||
if (CONFIG.localsearch.trigger === 'auto') {
|
||||
if (input) {
|
||||
input.addEventListener('input', inputEventFunction);
|
||||
// Show search results
|
||||
if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
|
||||
let hitCount = indexOfTitle.length + indexOfContent.length;
|
||||
// Sort index by position of keyword
|
||||
[indexOfTitle, indexOfContent].forEach(index => {
|
||||
index.sort((itemLeft, itemRight) => {
|
||||
if (itemRight.position !== itemLeft.position) {
|
||||
return itemRight.position - itemLeft.position;
|
||||
}
|
||||
return itemLeft.word.length - itemRight.word.length;
|
||||
});
|
||||
});
|
||||
|
||||
let slicesOfTitle = [];
|
||||
if (indexOfTitle.length !== 0) {
|
||||
let tmp = mergeIntoSlice(0, title.length, indexOfTitle, searchText);
|
||||
searchTextCount += tmp.searchTextCountInSlice;
|
||||
slicesOfTitle.push(tmp);
|
||||
}
|
||||
|
||||
let slicesOfContent = [];
|
||||
while (indexOfContent.length !== 0) {
|
||||
let item = indexOfContent[indexOfContent.length - 1];
|
||||
let {position, word} = item;
|
||||
// Cut out 100 characters
|
||||
let start = position - 20;
|
||||
let end = position + 80;
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end < position + word.length) {
|
||||
end = position + word.length;
|
||||
}
|
||||
if (end > content.length) {
|
||||
end = content.length;
|
||||
}
|
||||
let tmp = mergeIntoSlice(start, end, indexOfContent, searchText);
|
||||
searchTextCount += tmp.searchTextCountInSlice;
|
||||
slicesOfContent.push(tmp);
|
||||
}
|
||||
|
||||
// Sort slices in content by search text's count and hits' count
|
||||
slicesOfContent.sort((sliceLeft, sliceRight) => {
|
||||
if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
|
||||
return sliceRight.searchTextCount - sliceLeft.searchTextCount;
|
||||
} else if (sliceLeft.hits.length !== sliceRight.hits.length) {
|
||||
return sliceRight.hits.length - sliceLeft.hits.length;
|
||||
}
|
||||
return sliceLeft.start - sliceRight.start;
|
||||
});
|
||||
|
||||
// Select top N slices in content
|
||||
let upperBound = parseInt(CONFIG.localsearch.top_n_per_article ? CONFIG.localsearch.top_n_per_article : 1, 10);
|
||||
if (upperBound >= 0) {
|
||||
slicesOfContent = slicesOfContent.slice(0, upperBound);
|
||||
}
|
||||
|
||||
let resultItem = '';
|
||||
|
||||
if (slicesOfTitle.length !== 0) {
|
||||
resultItem += `<li><a href="${url}" class="search-result-title">${highlightKeyword(title, slicesOfTitle[0])}</a>`;
|
||||
} else {
|
||||
resultItem += `<li><a href="${url}" class="search-result-title">${title}</a>`;
|
||||
}
|
||||
|
||||
slicesOfContent.forEach(slice => {
|
||||
resultItem += `<a href="${url}"><p class="search-result">${highlightKeyword(content, slice)}...</p></a>`;
|
||||
});
|
||||
|
||||
resultItem += '</li>';
|
||||
resultItems.push({
|
||||
item: resultItem,
|
||||
id: resultItems.length,
|
||||
hitCount,
|
||||
searchTextCount
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (keywords.length === 1 && keywords[0] === '') {
|
||||
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x"></i></div>';
|
||||
} else if (resultItems.length === 0) {
|
||||
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x"></i></div>';
|
||||
} else {
|
||||
resultItems.sort((resultLeft, resultRight) => {
|
||||
if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
|
||||
return resultRight.searchTextCount - resultLeft.searchTextCount;
|
||||
} else if (resultLeft.hitCount !== resultRight.hitCount) {
|
||||
return resultRight.hitCount - resultLeft.hitCount;
|
||||
}
|
||||
return resultRight.id - resultLeft.id;
|
||||
});
|
||||
let searchResultList = '<ul class="search-result-list">';
|
||||
resultItems.forEach(result => {
|
||||
searchResultList += result.item;
|
||||
});
|
||||
searchResultList += '</ul>';
|
||||
resultContent.innerHTML = searchResultList;
|
||||
window.pjax && window.pjax.refresh(resultContent);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = () => {
|
||||
fetch(CONFIG.root + searchPath)
|
||||
.then(response => response.text())
|
||||
.then(res => {
|
||||
// Get the contents from search data
|
||||
isfetched = true;
|
||||
datas = isXml ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => {
|
||||
return {
|
||||
title: element.querySelector('title').textContent,
|
||||
content: element.querySelector('content').textContent,
|
||||
url: element.querySelector('url').textContent
|
||||
};
|
||||
}) : JSON.parse(res);
|
||||
// Only match articles with not empty titles
|
||||
datas = datas.filter(data => data.title).map(data => {
|
||||
data.title = data.title.trim();
|
||||
data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : '';
|
||||
if (CONFIG.localsearch.unescape) {
|
||||
data.content = unescapeHtml(data.content);
|
||||
}
|
||||
data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/');
|
||||
return data;
|
||||
});
|
||||
// Remove loading animation
|
||||
document.getElementById('no-result').innerHTML = '<i class="fa fa-search fa-5x"></i>';
|
||||
});
|
||||
};
|
||||
|
||||
if (CONFIG.localsearch.preload) {
|
||||
fetchData();
|
||||
}
|
||||
|
||||
} else {
|
||||
document.querySelector('.search-icon').addEventListener('click', inputEventFunction);
|
||||
input.addEventListener('keypress', event => {
|
||||
if (event.key === 'Enter') {
|
||||
inputEventFunction();
|
||||
}
|
||||
if (CONFIG.localsearch.trigger === 'auto') {
|
||||
if (input) {
|
||||
input.addEventListener('input', inputEventFunction);
|
||||
}
|
||||
|
||||
} else {
|
||||
document.querySelector('.search-icon').addEventListener('click', inputEventFunction);
|
||||
input.addEventListener('keypress', event => {
|
||||
if (event.key === 'Enter') {
|
||||
inputEventFunction();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle and trigger popup window
|
||||
document.querySelectorAll('.popup-trigger').forEach(element => {
|
||||
element.addEventListener('click', () => {
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.querySelector('.search-pop-overlay').style.display = 'block';
|
||||
input.focus();
|
||||
if (!isfetched) fetchData();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle and trigger popup window
|
||||
document.querySelectorAll('.popup-trigger').forEach(element => {
|
||||
element.addEventListener('click', () => {
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.querySelector('.search-pop-overlay').style.display = 'block';
|
||||
input.focus();
|
||||
if (!isfetched) fetchData();
|
||||
// Monitor main search box
|
||||
const onPopupClose = () => {
|
||||
document.body.style.overflow = '';
|
||||
document.querySelector('.search-pop-overlay').style.display = '';
|
||||
};
|
||||
|
||||
document.querySelector('.search-pop-overlay').addEventListener('click', event => {
|
||||
if (event.target === document.querySelector('.search-pop-overlay')) {
|
||||
onPopupClose();
|
||||
}
|
||||
});
|
||||
document.querySelector('.popup-btn-close').addEventListener('click', onPopupClose);
|
||||
window.addEventListener('pjax:success', onPopupClose);
|
||||
window.addEventListener('keyup', event => {
|
||||
if (event.key === 'Escape') {
|
||||
onPopupClose();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Monitor main search box
|
||||
const onPopupClose = () => {
|
||||
document.body.style.overflow = '';
|
||||
document.querySelector('.search-pop-overlay').style.display = '';
|
||||
};
|
||||
|
||||
document.querySelector('.search-pop-overlay').addEventListener('click', event => {
|
||||
if (event.target === document.querySelector('.search-pop-overlay')) {
|
||||
onPopupClose();
|
||||
}
|
||||
});
|
||||
document.querySelector('.popup-btn-close').addEventListener('click', onPopupClose);
|
||||
window.addEventListener('pjax:success', onPopupClose);
|
||||
window.addEventListener('keyup', event => {
|
||||
if (event.key === 'Escape') {
|
||||
onPopupClose();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue