Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: add several improvements to local search #1612

Merged
merged 4 commits into from
Apr 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@ algolia_search:
# Local search
local_search:
enable: false
# show top n results per article, show all results by setting to -1
top_n_per_article: 1

# External URL with BASE64 encrypt & decrypt
# Usage: {% exturl text url "title" %}
Expand Down
195 changes: 147 additions & 48 deletions layout/_third-party/search/localsearch.swig
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@
var isfetched = false;
// Search DB path;
var search_path = "{{ config.search.path }}";
if (search_path.length == 0) {
if (search_path.length === 0) {
search_path = "search.xml";
}
var path = "{{ config.root }}" + search_path;
// monitor main search box;

var onPopupClose = function (e) {
$('.popup').hide();
$('#local-search-input').val('');
$('.search-result-list').remove();
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
}

function proceedsearch() {
$("body")
.append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
.css('overflow', 'hidden');
$('.search-popup-overlay').click(onPopupClose);
$('.popup').toggle();
$('#local-search-input').focus();
}

// search function;
var searchFunc = function(path, search_id, content_id) {
'use strict';
Expand All @@ -37,69 +48,161 @@
var $input = document.getElementById(search_id);
var $resultContent = document.getElementById(content_id);
$input.addEventListener('input', function(){
var matchcounts = 0;
var str='<ul class=\"search-result-list\">';
var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
$resultContent.innerHTML = "";
if (this.value.trim().length > 1) {
var resultItems = [];
if (this.value.trim().length > 0) {
// perform local searching
datas.forEach(function(data) {
var isMatch = false;
var content_index = [];
var data_title = data.title.trim().toLowerCase();
var data_content = data.content.trim().replace(/<[^>]+>/g,"").toLowerCase();
var data_url = decodeURIComponent(data.url);
var index_title = -1;
var index_content = -1;
var first_occur = -1;
// only match artiles with not empty titles and contents
if(data_title != '') {
var hitCountInArticle = 0;
var title = data.title.trim();
var titleInLowerCase = title.toLowerCase();
var content = data.content.trim().replace(/<[^>]+>/g,"");
var contentInLowerCase = content.toLowerCase();
var articleUrl = decodeURIComponent(data.url);
var indexOfTitle = [];
var indexOfContent = [];
// only match articles with not empty titles
if(title != '') {
keywords.forEach(function(keyword, i) {
index_title = data_title.indexOf(keyword);
index_content = data_content.indexOf(keyword);
if( index_title >= 0 || index_content >= 0 ){
isMatch = true;
if (i == 0) {
first_occur = index_content;
function getIndexByWord(word, text, caseSensitive) {
var wordLen = word.length;
if (wordLen === 0) {
return [];
}
var startPosition = 0, position = [], index = [];
if (!caseSensitive) {
text = text.toLowerCase();
word = word.toLowerCase();
}
while ((position = text.indexOf(word, startPosition)) > -1) {
index.push({position: position, word: word});
startPosition = position + wordLen;
}
return index;
}

indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
});
if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
isMatch = true;
hitCountInArticle = indexOfTitle.length + indexOfContent.length;
}
}

// show search results

if (isMatch) {
matchcounts += 1;
str += "<li><a href='"+ data_url +"' class='search-result-title'>"+ data_title +"</a>";
var content = data.content.trim().replace(/<[^>]+>/g,"");
if (first_occur >= 0) {
var resultItem = '';

function highlightKeyword(text, start, end, index) {
var item = index[index.length - 1];
var position = item.position;
var word = item.word;

var matchText = text.substring(start, end);
var matchResult = [];
var prevEnd = 0;
while (position + word.length <= end && index.length != 0) {

// highlight keyword

var wordBegin = position - start;
var wordEnd = position - start + word.length;
matchResult.push(matchText.substring(prevEnd, wordBegin));
matchResult.push("<b class=\"search-keyword\">" + matchText.substring(wordBegin, wordEnd) + "</b>");

// move to next position of hit

index.pop();
prevEnd = wordEnd;
while (index.length != 0) {
item = index[index.length - 1];
position = item.position;
word = item.word;
if (prevEnd > position - start) {
index.pop();
} else {
break;
}
}
}
matchResult.push(matchText.substring(prevEnd));
return matchResult.join('');
}

// sort index by position of keyword

indexOfTitle.sort(function (itemLeft, itemRight) {
return itemRight.position - itemLeft.position;
});

indexOfContent.sort(function (itemLeft, itemRight) {
return itemRight.position - itemLeft.position;
});

// highlight title

if (indexOfTitle.length != 0) {
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, 0, title.length, indexOfTitle) + "</a>";
} else {
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
}

// highlight content

var resultUpperBound = parseInt({{ theme.local_search.top_n_per_article }});
var withoutUpperBound = false;
if (resultUpperBound === -1) {
withoutUpperBound = true;
}
var currentResultNum = 0;
while (indexOfContent.length != 0 && (withoutUpperBound || (currentResultNum < resultUpperBound))) {
var item = indexOfContent[indexOfContent.length - 1];
var position = item.position;
var word = item.word;
// cut out 100 characters
var start = first_occur - 20;
var end = first_occur + 80;
var start = position - 20;
var end = position + 80;
if(start < 0){
start = 0;
}
if(start == 0){
end = 50;
if (end < position + word.length) {
end = position + word.length;
}
if(end > content.length){
end = content.length;
}
var match_content = content.substring(start, end);
// highlight all keywords
keywords.forEach(function(keyword){
var regS = new RegExp(keyword, "gi");
match_content = match_content.replace(regS, "<b class=\"search-keyword\">"+keyword+"</b>");
});

str += "<p class=\"search-result\">" + match_content +"...</p>"
resultItem += "<a href='" + articleUrl + "'>" +
"<p class=\"search-result\">" + highlightKeyword(content, start, end, indexOfContent) +
"...</p>" + "</a>";
currentResultNum++;
}
str += "</li>";
resultItem += "</li>";
resultItems.push({item: resultItem, hitCount: hitCountInArticle, id: resultItems.length});
}
})
};
if (keywords.length === 1 && keywords[0] === "") {
$resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
} else if (resultItems.length === 0) {
$resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
} else {
resultItems.sort(function (resultLeft, resultRight) {
if (resultLeft.hitCount != resultRight.hitCount) {
return resultRight.hitCount - resultLeft.hitCount;
} else {
return resultLeft.id - resultRight.id;
}
})};
str += "</ul>";
if (matchcounts == 0) { str = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>' }
if (keywords == "") { str = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>' }
$resultContent.innerHTML = str;
});
var searchResultList = '<ul class=\"search-result-list\">';
resultItems.forEach(function (result, i) {
searchResultList += result.item;
})
searchResultList += "</ul>";
$resultContent.innerHTML = searchResultList;
}
});
proceedsearch();
}
Expand All @@ -108,18 +211,14 @@
// handle and trigger popup window;
$('.popup-trigger').click(function(e) {
e.stopPropagation();
if (isfetched == false) {
if (isfetched === false) {
searchFunc(path, 'local-search-input', 'local-search-result');
} else {
proceedsearch();
};
});

$('.popup-btn-close').click(function(e){
$('.popup').hide();
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
});
$('.popup-btn-close').click(onPopupClose);
$('.popup').click(function(e){
e.stopPropagation();
});
Expand Down
6 changes: 3 additions & 3 deletions source/css/_common/components/third-party/localsearch.styl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@

.search-keyword
border-bottom: 1px dashed #f00
font-size: 14px
font-weight: bold
color: #f00

Expand All @@ -62,7 +61,7 @@

.local-search-input-wrapper
display: inline-block
width: calc(100% - 60px)
width: calc(100% - 90px)
height: 36px
line-height: 36px
padding: 0 5px
Expand All @@ -83,6 +82,8 @@
color: #999
height: 36px
width: 18px
padding-left: 10px
padding-right: 10px

.search-icon
float: left
Expand All @@ -91,7 +92,6 @@
border-left: 1px solid #eee
float: right
cursor: pointer
padding-left: 10px

#no-result
position: absolute
Expand Down