/** * Insight search plugin * @author PPOffice { @link https://github.com/ppoffice } */ (function ($, CONFIG) { $main = $('.ins-search'); $container = $('.ins-section-container'); $main.parent().remove('.ins-search'); $('body').append($main); $(document).on('click focus', '.search-form-input', function () { $main.addClass('show'); $main.find('.ins-search-input').focus(); }).on('click', '.ins-search-item', function () { location.href=$(this).attr('data-url'); }).on('click', '.ins-close', function () { $main.removeClass('show'); }); 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, 5), 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])); } } $.getJSON(CONFIG.CONTENT_URL, function (json) { if (location.hash.trim() == '#ins-search') { $main.addClass('show'); } $('.ins-search-input').on('input', function () { var keywords = $(this).val(); searchResultToDOM(search(json, keywords)); }); $('.ins-search-input').trigger('input'); }); })(jQuery, window.INSIGHT_CONFIG);