Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
426 lines (389 sloc) 13.5 KB
<style>
.local-search-popup {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 0;
background: rgba(255, 255, 255, .9);
color: #333;
z-index: 9999;
border-radius: 5px;
overflow: scroll;
}
#local-search-input {
width: 100%;
border: none;
outline: none;
border-bottom: 1px solid #151515;
background-color: initial;
}
.search-result-list {
list-style: none;
padding-left: 0;
}
.search-result-list > li {
margin-top: 15px;
border-bottom: 1px solid #ddd;
transition: all ease .3s;
}
.search-result-list > li:hover {
border-bottom: 1px solid gray;
}
.search-result-title {
font-size: 16px;
}
.search-result {
line-height: 20px;
}
.search-keyword {
font-weight: normal;
color: #c00;
}
@media (min-width: 890px) {
.popup-btn-close {
position: absolute;
top: 15px;
left: 35px;
border: 1px solid #151515;
padding: 0px 10px;
border-top-left-radius: 8px;
cursor: pointer;
transition: all ease .3s;
}
.popup-btn-close:hover {
background: #151515;
opacity: .9;
color: #fff;
}
}
@media (max-width: 890px) {
.popup-btn-close {
font-size: 0;
position: fixed;
right: 20px;
bottom: 50px;
width: 50px;
height: 50px;
background: #fff;
border-radius: 50%;
box-shadow: 1px 1px 5px #888;
cursor: pointer;
}
.popup-btn-close::after {
content: '';
color: #151515;
position: absolute;
top: 0;
left: 0;
font-size: 20px;
width: 100%;
height: 100%;
line-height: 50px;
text-align: center;
}
}
</style>
<div class="popup search-popup local-search-popup">
<span class="popup-btn-close">
ESC
</span>
<div class="container">
<div class="col-md-8 col-md-offset-2">
<div class="local-search-header clearfix">
<span class="search-icon">
<i class="fa fa-search"></i>
</span>
<div class="local-search-input-wrapper">
<input autocomplete="off" placeholder="Search..." type="text" id="local-search-input">
</div>
</div>
<div id="local-search-result"></div>
</div>
</div>
</div>
<script src="<%- config.root %>js/ziploader.js"></script>
<% if (theme.local_search.enable) { %>
<script type="text/javascript">
// Popup Window;
var isfetched = false;
var isXml = true;
// Search DB path;
var search_path = "<%= config.search.path %>";
if (search_path.length === 0) {
search_path = "search.xml";
} else if (/json$/i.test(search_path)) {
isXml = false;
}
// monitor main search box;
var onPopupClose = function (e) {
$('.popup').fadeOut(300);
$('#local-search-input').val('');
$('.search-result-list').remove();
$('#no-result').remove();
$('body').css('overflow', '');
}
function proceedsearch() {
$('.popup').fadeIn(300);
var $localSearchInput = $('#local-search-input');
$localSearchInput.attr("autocapitalize", "none");
$localSearchInput.attr("autocorrect", "off");
$localSearchInput.focus();
}
// get search zip version
$.get('<%- config.root %><%= config.search.versionPath %>?t=' + (+new Date()), function(res) {
if (localStorage.getItem('searchVersion') !== res) {
localStorage.setItem('searchVersion', res);
initSearchJson();
}
});
function initSearchJson () {
initLoad(['<%- config.root %><%= config.search.zipPath %>'], {
loadOptions: {
success: function(obj) {
localStorage.setItem('searchJson', obj['<%= config.search.path %>'])
},
error: function(e) {
return console.log(e)
}
},
returnOptions: {
'json': TYPE_TEXT
},
mimeOptions:{
'json':'application/json'
}
})
}
// search function;
var searchFunc = function(search_id, content_id) {
'use strict';
isfetched = true;
var datas = JSON.parse(localStorage.getItem('searchJson'));
console.log(search_id)
var input = document.getElementById(search_id);
var resultContent = document.getElementById(content_id);
var inputEventFunction = function() {
var searchText = input.value.trim().toLowerCase();
var keywords = searchText.split(/[\s\-]+/);
if (keywords.length > 1) {
keywords.push(searchText);
}
var resultItems = [];
if (searchText.length > 0) {
// perform local searching
datas.forEach(function(data) {
var isMatch = false;
var hitCount = 0;
var searchTextCount = 0;
var title = data.title ? data.title.trim() : '';
var titleInLowerCase = title.toLowerCase();
var content = data.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
keywords.forEach(function(keyword) {
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;
hitCount = indexOfTitle.length + indexOfContent.length;
}
// show search results
if (isMatch) {
// sort index by position of keyword
[indexOfTitle, indexOfContent].forEach(function (index) {
index.sort(function (itemLeft, itemRight) {
if (itemRight.position !== itemLeft.position) {
return itemRight.position - itemLeft.position;
} else {
return itemLeft.word.length - itemRight.word.length;
}
});
});
// merge hits into slices
function mergeIntoSlice(text, start, end, index) {
var item = index[index.length - 1];
var position = item.position;
var word = item.word;
var hits = [];
var searchTextCountInSlice = 0;
while (position + word.length <= end && index.length != 0) {
if (word === searchText) {
searchTextCountInSlice++;
}
hits.push({position: position, length: word.length});
var 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;
}
}
}
searchTextCount += searchTextCountInSlice;
return {
hits: hits,
start: start,
end: end,
searchTextCount: searchTextCountInSlice
};
}
var slicesOfTitle = [];
if (indexOfTitle.length != 0) {
slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
}
var slicesOfContent = [];
while (indexOfContent.length != 0) {
var item = indexOfContent[indexOfContent.length - 1];
var position = item.position;
var word = item.word;
// cut out 100 characters
var start = position - 20;
var end = position + 80;
if(start < 0){
start = 0;
}
if (end < position + word.length) {
end = position + word.length;
}
if(end > content.length){
end = content.length;
}
slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
}
// sort slices in content by search text's count and hits' count
slicesOfContent.sort(function (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;
} else {
return sliceLeft.start - sliceRight.start;
}
});
// select top N slices in content
var upperBound = parseInt('<%= theme.local_search.top_n_per_article %>');
if (upperBound >= 0) {
slicesOfContent = slicesOfContent.slice(0, upperBound);
}
// highlight title and content
function highlightKeyword(text, slice) {
var result = '';
var prevEnd = slice.start;
slice.hits.forEach(function (hit) {
result += text.substring(prevEnd, hit.position);
var 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;
}
var resultItem = '';
if (slicesOfTitle.length != 0) {
resultItem += "<li><a target='_blank' href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
} else {
resultItem += "<li><a target='_blank' href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
}
slicesOfContent.forEach(function (slice) {
resultItem += "<a target='_blank' href='" + articleUrl + "'>" +
"<p class=\"search-result\">" + highlightKeyword(content, slice) +
"...</p>" + "</a>";
});
resultItem += "</li>";
resultItems.push({
item: resultItem,
searchTextCount: searchTextCount,
hitCount: hitCount,
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.searchTextCount !== resultRight.searchTextCount) {
return resultRight.searchTextCount - resultLeft.searchTextCount;
} else if (resultLeft.hitCount !== resultRight.hitCount) {
return resultRight.hitCount - resultLeft.hitCount;
} else {
return resultRight.id - resultLeft.id;
}
});
var searchResultList = '<ul class=\"search-result-list\">';
resultItems.forEach(function (result) {
searchResultList += result.item;
})
searchResultList += "</ul>";
resultContent.innerHTML = searchResultList;
}
}
if ('auto' === '<%= theme.local_search.trigger %>') {
input.addEventListener('input', inputEventFunction);
} else {
$('.search-icon').click(inputEventFunction);
input.addEventListener('keypress', function (event) {
if (event.keyCode === 13) {
inputEventFunction();
}
});
}
// remove loading animation
$('body').css('overflow', '');
proceedsearch();
}
// handle and trigger popup window;
$('.popup-trigger').click(function(e) {
e.stopPropagation();
if (isfetched === false) {
$('.sb-close').click();
searchFunc('local-search-input', 'local-search-result');
} else {
proceedsearch();
};
});
$('.popup-btn-close').click(onPopupClose);
$('.popup').click(function(e){
e.stopPropagation();
});
$(document).on('keyup', function (event) {
var shouldDismissSearchPopup = event.which === 27 &&
$('.search-popup').is(':visible');
if (shouldDismissSearchPopup) {
onPopupClose();
}
});
</script>
<% } %>