| const localSearch = function(pjax) { |
| |
| |
| if(CONFIG.localSearch === null) |
| return |
| |
| if(!siteSearch) { |
| siteSearch = BODY.createChild('div', { |
| id: 'search', |
| innerHTML: '<div class="inner"><div class="header"><span class="icon"><i class="ic i-search"></i></span><div class="search-input-container"><input class="search-input"autocompvare="off"placeholder="'+LOCAL.search.placeholder+'"spellcheck="false"type="text"id="local-search-input"></div><span class="close-btn"><i class="ic i-times-circle"></i></span></div><div class="results"id="search-results"><div class="inner"><div id="search-stats"></div><div id="search-hits"></div><div id="search-pagination"></div></div></div></div></div>' |
| }); |
| } |
| |
| var isFetched = false; |
| var datas; |
| var isXml = true; |
| var current_page = 0; |
| var pageSize = parseInt(CONFIG.localSearch.pageSize, 10); |
| if(isNaN(pageSize)) pageSize = 10; |
| var total_pages = 0; |
| var max_page_on_show = 7; |
| var start_page = 0; |
| var end_page = 0; |
| var resultItems = []; |
| |
| |
| var searchPath = CONFIG.localSearch.path; |
| if (searchPath.length == 0) { |
| searchPath = 'search.xml'; |
| } else if (searchPath.endsWith('json')) { |
| isXml = false; |
| } |
| |
| const input = $('.search-input'); |
| const resultContent = document.getElementById('search-hits'); |
| const paginationContent = document.getElementById('search-pagination'); |
| |
| const getIndexByWord = function(word, text, caseSensitive) { |
| if (CONFIG.localSearch.unescape) { |
| var div = document.createElement('div'); |
| div.innerText = word; |
| word = div.innerHTML; |
| } |
| var wordLen = word.length; |
| if (wordLen === 0) { |
| return []; |
| } |
| var startPosition = 0; |
| var position = []; |
| var 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; |
| }; |
| |
| |
| const mergeIntoSlice = function(start, end, index, searchText) { |
| 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; |
| |
| |
| 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:hits, |
| start:start, |
| end:end, |
| searchTextCount: searchTextCountInSlice |
| }; |
| } |
| |
| |
| const highlightKeyword = function(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 += '<mark>'+ text.substring(hit.position, end)+'</mark>'; |
| prevEnd = end; |
| }); |
| result += text.substring(prevEnd, slice.end); |
| return result; |
| }; |
| |
| const pagination = function() { |
| |
| const addPrevPage = function(current_page) { |
| var classContent = ''; |
| var numberContent = ''; |
| if (current_page === 0) { |
| classContent = '#search-pagination pagination-item disabled-item'; |
| numberContent = '<span class="#search-pagination page-number"><i class="ic i-angle-left"></i></span>'; |
| } else { |
| classContent = '#search-pagination pagination-item'; |
| numberContent = '<a class="#search-pagination page-number" aria-label="Prev" href="#"><i class="ic i-angle-left"></i></a>'; |
| } |
| var prevPage = '<li class="'+ classContent +'" id="prev-page">'+ numberContent+'</li>'; |
| return prevPage; |
| }; |
| |
| const addNextPage = function(current_page) { |
| var classContent = ''; |
| var numberContent = ''; |
| if ((current_page + 1) === total_pages) { |
| classContent = '#search-pagination pagination-item disabled-item'; |
| numberContent = '<span class="#search-pagination page-number"><i class="ic i-angle-right"></i></span>'; |
| } else { |
| classContent = '#search-pagination pagination-item'; |
| numberContent = '<a class="#search-pagination page-number"aria-label="Next"href="#"><i class="ic i-angle-right"></i></a>'; |
| } |
| var nextPage = '<li class="' + classContent +'"id="next-page">'+ numberContent +'</li>'; |
| return nextPage; |
| }; |
| |
| const addPage = function(index, current_page) { |
| var classContent = ''; |
| var numberContent = '<a class="#search-pagination page-number"aria-label="'+ (index + 1) +'"href="#">'+(index+1)+'</a>'; |
| if (index === current_page) { |
| classContent = '#search-pagination pagination-item current'; |
| } else { |
| classContent = '#search-pagination pagination-item'; |
| } |
| var page = '<li class="'+classContent+'" id="page-'+(index + 1)+'">'+numberContent+'</li>'; |
| return page; |
| } |
| |
| const addPaginationEvents = function(start_page, end_page) { |
| if (total_pages <= 0) { |
| return; |
| } |
| const onPrevPageClick = function(event) { |
| if (current_page > 0) { |
| current_page -= 1; |
| } |
| if (current_page < start_page) { |
| start_page = current_page; |
| end_page = Math.min(end_page, start_page + max_page_on_show); |
| } |
| pagination(); |
| }; |
| const onNextPageClick = function(event) { |
| if ((current_page + 1) < total_pages) { |
| current_page += 1; |
| } |
| if (current_page > end_page) { |
| end_page = current_page; |
| start_page = Math.max(0, end_page - max_page_on_show); |
| } |
| pagination(); |
| }; |
| const onPageClick = function(event) { |
| var page_number = parseInt(event.target.ariaLabel); |
| current_page = page_number - 1; |
| pagination(); |
| }; |
| |
| var prevPage = document.getElementById('prev-page'); |
| if(prevPage != null)prevPage.addEventListener('click', onPrevPageClick); |
| |
| var nextPage = document.getElementById('next-page'); |
| if(nextPage != null) nextPage.addEventListener('click', onNextPageClick); |
| for (var i = start_page; i < end_page; i += 1) { |
| var page = document.getElementById('page-'+(i + 1)); |
| if(page != null)page.addEventListener('click', onPageClick); |
| } |
| }; |
| |
| |
| |
| paginationContent.innerHTML = ''; |
| |
| var begin_index = Math.min(current_page * pageSize, resultItems.length); |
| var end_index = Math.min(begin_index + pageSize, resultItems.length); |
| |
| resultContent.innerHTML = resultItems.slice(begin_index, end_index).map(function(result) {return result.item}).join(''); |
| |
| start_page = Math.max(0, total_pages - max_page_on_show); |
| end_page = start_page + Math.min(total_pages, max_page_on_show); |
| var pageContent = '<div class="#search-pagination">'; |
| pageContent += '<div class="#search-pagination pagination">'; |
| pageContent += '<ul>'; |
| if (total_pages > 0) { |
| |
| pageContent += addPrevPage(current_page); |
| for (var i = start_page; i < end_page; i += 1) { |
| pageContent += addPage(i, current_page); |
| } |
| |
| pageContent += addNextPage(current_page); |
| } |
| pageContent += '</ul>'; |
| pageContent += '</div>'; |
| pageContent += '</div>'; |
| paginationContent.innerHTML = pageContent; |
| addPaginationEvents(start_page, end_page); |
| resultContent.scrollTop = 0; |
| window.pjax && window.pjax.refresh(resultContent); |
| }; |
| |
| |
| |
| const inputEventFunction = function() { |
| if (!isFetched) { |
| console.log("Data not fetched."); |
| return; |
| } |
| |
| var searchText = input.value.trim().toLowerCase(); |
| var keywords = searchText.split(/[-\s]+/); |
| |
| if (keywords.length > 1) { |
| keywords.push(searchText); |
| } |
| |
| resultItems = []; |
| if (searchText.length > 0) { |
| |
| datas.forEach(function(index) { |
| |
| var categories = index.categories, title=index.title, content=index.content, url=index.url; |
| var titleInLowerCase = title.toLowerCase(); |
| var contentInLowerCase = content.toLowerCase(); |
| var indexOfTitle = []; |
| var indexOfContent = []; |
| var searchTextCount = 0; |
| keywords.forEach( function(keyword) { |
| indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false)); |
| indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false)); |
| }); |
| |
| |
| |
| if (indexOfTitle.length > 0 || indexOfContent.length > 0) { |
| var hitCount = indexOfTitle.length + indexOfContent.length; |
| |
| [indexOfTitle, indexOfContent].forEach(function(index) { |
| index.sort(function(itemLeft, itemRight) { |
| if (itemRight.position !== itemLeft.position) { |
| return itemRight.position - itemLeft.position; |
| } |
| return itemLeft.word.length - item.word.length; |
| }); |
| }); |
| |
| var slicesOfTitle = []; |
| if (indexOfTitle.length !== 0) { |
| var tmp = mergeIntoSlice(0, title.length, indexOfTitle, searchText); |
| searchTextCount += tmp.searchTextCountInSlice; |
| slicesOfTitle.push(tmp); |
| } |
| |
| var slicesOfContent = []; |
| while (indexOfContent.length !== 0) { |
| var item = indexOfContent[indexOfContent.length - 1]; |
| var position = item.position; |
| var word = item.word; |
| |
| var start = position - 20; |
| var end = position + 30; |
| if (start < 0) { |
| start = 0; |
| } |
| if (end < position + word.length) { |
| end = position + word.length; |
| } |
| if (end > content.length) { |
| end = content.length; |
| } |
| var tmp = mergeIntoSlice(start, end, indexOfContent, searchText); |
| searchTextCount += tmp.searchTextCountInSlice; |
| slicesOfContent.push(tmp); |
| } |
| |
| |
| |
| 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; |
| } |
| return sliceLeft.start - sliceRight.start; |
| }); |
| |
| |
| var upperBound = parseInt(CONFIG.localSearch.pageSize, 10); |
| if (upperBound >= 0) { |
| slicesOfContent = slicesOfContent.slice(0, upperBound); |
| } |
| |
| |
| var resultItem = ''; |
| resultItem += '<div class="#search-hits item">'; |
| |
| |
| resultItem += '<li>' |
| |
| var cats = categories !== undefined ? '<span>' + categories.join('<i class="ic i-angle-right"></i>') + '</span>' : '<span>No categories</span>'; |
| resultItem += '<a href="'+url+'">' + cats; |
| if (slicesOfTitle.length !== 0) { |
| |
| resultItem += '<b>'+highlightKeyword(title, slicesOfTitle[0])+'</b><br>'; |
| } else { |
| |
| resultItem += '<b>'+title+'</b><br>'; |
| } |
| |
| slicesOfContent.forEach(function(slice) { |
| return resultItem += '<li class="#search-hits subitem">'+highlightKeyword(content, slice)+' ...</li>'; |
| }); |
| |
| resultItem += '</a>'; |
| resultItem += '</li>'; |
| |
| resultItem += '</div>'; |
| resultItems.push({ |
| item: resultItem, |
| id : resultItems.length, |
| hitCount:hitCount, |
| searchTextCount:searchTextCount |
| }); |
| } |
| }); |
| } |
| |
| if (keywords.length === 1 && keywords[0] === '') { |
| resultContent.innerHTML = '<div id="no-result"><i></i></div>'; |
| } else if (resultItems.length === 0) { |
| resultContent.innerHTML = '<div id="no-result"><i></i></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; |
| } |
| return resultRight.id - resultLeft.id; |
| }); |
| } |
| |
| |
| total_pages = Math.ceil(resultItems.length / pageSize); |
| pagination(); |
| } |
| |
| |
| const fetchData = function() { |
| fetch(CONFIG.root + searchPath) |
| .then(function(response) {return response.text()} ) |
| .then( function(res) { |
| |
| isFetched = true; |
| datas = isXml ? [new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map( function(element) { |
| return { |
| title : element.querySelector('title').textContent, |
| content: element.querySelector('content').textContent, |
| url : element.querySelector('url').textContent |
| }; |
| }) : JSON.parse(res); |
| |
| datas = datas.filter(function(data) {return data.title} ).map( function(data) { |
| data.title = data.title.trim(); |
| data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''; |
| data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/'); |
| return data; |
| }); |
| |
| document.getElementById('search-hits').innerHTML = '<i></i>'; |
| inputEventFunction(); |
| }); |
| }; |
| |
| if (CONFIG.localSearch.preload) { |
| console.log("fetch data."); |
| fetchData(); |
| } |
| |
| if (CONFIG.localSearch.trigger === 'auto') { |
| input.addEventListener('input', inputEventFunction); |
| } else { |
| document.querySelector('.search-icon').addEventListener('click', inputEventFunction); |
| input.addEventListener('keypress',function(event) { |
| if (event.key === 'Enter') { |
| inputEventFunction(); |
| } |
| }); |
| } |
| |
| |
| document.querySelectorAll('.popup-trigger').forEach( function(element) { |
| element.addEventListener('click', function() { |
| document.body.style.overflow = 'hidden'; |
| document.querySelector('.search-pop-overlay').classList.add('search-active'); |
| input.focus(); |
| if (!isFetched) fetchData(); |
| }); |
| }); |
| |
| |
| $.each('.search', function(element) { |
| element.addEventListener('click', function() { |
| document.body.style.overflow = 'hidden'; |
| transition(siteSearch, 'shrinkIn', function() { |
| $('.search-input').focus(); |
| }) |
| }); |
| }); |
| |
| |
| const onPopupClose = function() { |
| document.body.style.overflow = ''; |
| transition(siteSearch, 0); |
| }; |
| |
| siteSearch.addEventListener('click', function(event) { |
| if (event.target === siteSearch) { |
| onPopupClose(); |
| } |
| }); |
| |
| $('.close-btn').addEventListener('click', onPopupClose); |
| window.addEventListener('pjax:success', onPopupClose); |
| window.addEventListener('keyup', function(event) { |
| if (event.key === 'Escape') { |
| onPopupClose(); |
| } |
| }); |
| |
| }; |