Skip to content

Commit

Permalink
Merge pull request #1612 from uchuhimo/feature-local-search
Browse files Browse the repository at this point in the history
enhancement: add several improvements to local search
  • Loading branch information
ivan-nginx committed Apr 22, 2017
2 parents b74ddd8 + a023c30 commit 340c984
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 51 deletions.
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

0 comments on commit 340c984

Please sign in to comment.