From cfa3cf21a5adb56dd14ec047d8ef544821bf31a8 Mon Sep 17 00:00:00 2001 From: kaze0617 <1515097306@qq.com> Date: Tue, 7 Apr 2020 10:33:19 +0800 Subject: [PATCH] update --- js/InsightSearch.js | 243 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 js/InsightSearch.js diff --git a/js/InsightSearch.js b/js/InsightSearch.js new file mode 100644 index 0000000..00128ce --- /dev/null +++ b/js/InsightSearch.js @@ -0,0 +1,243 @@ +/** + * Insight search plugin + * @author PPOffice { @link https://github.com/ppoffice } + */ +(function ($, CONFIG) { + var $main = $('.ins-search'); + var $input = $main.find('.ins-search-input'); + var $wrapper = $main.find('.ins-section-wrapper'); + var $container = $main.find('.ins-section-container'); + $main.parent().remove('.ins-search'); + $('body').append($main); + + function section (title) { + return $('
').addClass('ins-section') + .append($('
').addClass('ins-section-header').text(title)); + } + + function searchItem (icon, title, slug, preview, url) { + return $('
').addClass('ins-selectable').addClass('ins-search-item') + .append($('
').append($('').addClass('fa').addClass('fa-' + icon)).append(title != null && title != '' ? title : CONFIG.TRANSLATION['UNTITLED']) + .append(slug ? $('').addClass('ins-slug').text(slug) : null)) + .append(preview ? $('

').addClass('ins-search-preview').text(preview) : null) + .attr('data-url', url); + } + + function sectionFactory (type, array) { + var sectionTitle; + var $searchItems; + if (array.length === 0) return null; + sectionTitle = CONFIG.TRANSLATION[type]; + switch (type) { + case 'POSTS': + case 'PAGES': + $searchItems = array.map(function (item) { + // Use config.root instead of permalink to fix url issue + return searchItem('file', item.title, null, item.text.slice(0, 150), CONFIG.ROOT_URL + item.path); + }); + break; + case 'CATEGORIES': + case 'TAGS': + $searchItems = array.map(function (item) { + return searchItem(type === 'CATEGORIES' ? 'folder' : 'tag', item.name, item.slug, null, item.permalink); + }); + break; + default: + return null; + } + return section(sectionTitle).append($searchItems); + } + + function extractToSet (json, key) { + var values = {}; + var entries = json.pages.concat(json.posts); + entries.forEach(function (entry) { + if (entry[key]) { + entry[key].forEach(function (value) { + values[value.name] = value; + }); + } + }); + var result = []; + for (var key in values) { + result.push(values[key]); + } + return result; + } + + function parseKeywords (keywords) { + return keywords.split(' ').filter(function (keyword) { + return !!keyword; + }).map(function (keyword) { + return keyword.toUpperCase(); + }); + } + + /** + * Judge if a given post/page/category/tag contains all of the keywords. + * @param Object obj Object to be weighted + * @param Array fields Object's fields to find matches + */ + function filter (keywords, obj, fields) { + var result = false; + var keywordArray = parseKeywords(keywords); + var containKeywords = keywordArray.filter(function (keyword) { + var containFields = fields.filter(function (field) { + if (!obj.hasOwnProperty(field)) + return false; + if (obj[field].toUpperCase().indexOf(keyword) > -1) + return true; + }); + if (containFields.length > 0) + return true; + return false; + }); + return containKeywords.length === keywordArray.length; + } + + function filterFactory (keywords) { + return { + POST: function (obj) { + return filter(keywords, obj, ['title', 'text']); + }, + PAGE: function (obj) { + return filter(keywords, obj, ['title', 'text']); + }, + CATEGORY: function (obj) { + return filter(keywords, obj, ['name', 'slug']); + }, + TAG: function (obj) { + return filter(keywords, obj, ['name', 'slug']); + } + }; + } + + /** + * Calculate the weight of a matched post/page/category/tag. + * @param Object obj Object to be weighted + * @param Array fields Object's fields to find matches + * @param Array weights Weight of every field + */ + function weight (keywords, obj, fields, weights) { + var value = 0; + parseKeywords(keywords).forEach(function (keyword) { + var pattern = new RegExp(keyword, 'img'); // Global, Multi-line, Case-insensitive + fields.forEach(function (field, index) { + if (obj.hasOwnProperty(field)) { + var matches = obj[field].match(pattern); + value += matches ? matches.length * weights[index] : 0; + } + }); + }); + return value; + } + + function weightFactory (keywords) { + return { + POST: function (obj) { + return weight(keywords, obj, ['title', 'text'], [3, 1]); + }, + PAGE: function (obj) { + return weight(keywords, obj, ['title', 'text'], [3, 1]); + }, + CATEGORY: function (obj) { + return weight(keywords, obj, ['name', 'slug'], [1, 1]); + }, + TAG: function (obj) { + return weight(keywords, obj, ['name', 'slug'], [1, 1]); + } + }; + } + + function search (json, keywords) { + var WEIGHTS = weightFactory(keywords); + var FILTERS = filterFactory(keywords); + var posts = json.posts; + // var pages = json.pages; + var tags = extractToSet(json, 'tags'); + var categories = extractToSet(json, 'categories'); + return { + posts: posts.filter(FILTERS.POST).sort(function (a, b) { return WEIGHTS.POST(b) - WEIGHTS.POST(a); }).slice(0, 20), + // pages: pages.filter(FILTERS.PAGE).sort(function (a, b) { return WEIGHTS.PAGE(b) - WEIGHTS.PAGE(a); }).slice(0, 5), + categories: categories.filter(FILTERS.CATEGORY).sort(function (a, b) { return WEIGHTS.CATEGORY(b) - WEIGHTS.CATEGORY(a); }).slice(0, 5), + tags: tags.filter(FILTERS.TAG).sort(function (a, b) { return WEIGHTS.TAG(b) - WEIGHTS.TAG(a); }).slice(0, 5) + }; + } + + function searchResultToDOM (searchResult) { + $container.empty(); + for (var key in searchResult) { + $container.append(sectionFactory(key.toUpperCase(), searchResult[key])); + } + } + + function scrollTo ($item) { + if ($item.length === 0) return; + var wrapperHeight = $wrapper[0].clientHeight; + var itemTop = $item.position().top - $wrapper.scrollTop(); + var itemBottom = $item[0].clientHeight + $item.position().top; + if (itemBottom > wrapperHeight + $wrapper.scrollTop()) { + $wrapper.scrollTop(itemBottom - $wrapper[0].clientHeight); + } + if (itemTop < 0) { + $wrapper.scrollTop($item.position().top); + } + } + + function selectItemByDiff (value) { + var $items = $.makeArray($container.find('.ins-selectable')); + var prevPosition = -1; + $items.forEach(function (item, index) { + if ($(item).hasClass('active')) { + prevPosition = index; + return; + } + }); + var nextPosition = ($items.length + prevPosition + value) % $items.length; + $($items[prevPosition]).removeClass('active'); + $($items[nextPosition]).addClass('active'); + scrollTo($($items[nextPosition])); + } + + function gotoLink ($item) { + if ($item && $item.length) { + location.href = $item.attr('data-url'); + } + } + + $.getJSON(CONFIG.CONTENT_URL, function (json) { + if (location.hash.trim() === '#ins-search') { + $main.addClass('show'); + } + $input.on('input', function () { + var keywords = $(this).val(); + searchResultToDOM(search(json, keywords)); + }); + $input.trigger('input'); + }); + + + $(document).on('click focus', '.search-field', function () { + $main.addClass('show'); + $main.find('.ins-search-input').focus(); + }).on('click focus', '.search-form-submit', function () { + $main.addClass('show'); + $main.find('.ins-search-input').focus(); + }).on('click', '.ins-search-item', function () { + gotoLink($(this)); + }).on('click', '.ins-close', function () { + $main.removeClass('show'); + }).on('keydown', function (e) { + if (!$main.hasClass('show')) return; + switch (e.keyCode) { + case 27: // ESC + $main.removeClass('show'); break; + case 38: // UP + selectItemByDiff(-1); break; + case 40: // DOWN + selectItemByDiff(1); break; + case 13: //ENTER + gotoLink($container.find('.ins-selectable.active').eq(0)); break; + } + }); +})(jQuery, window.INSIGHT_CONFIG); \ No newline at end of file