diff --git a/include/register.js b/include/register.js
index 6a5a4b77d..4c656b70c 100644
--- a/include/register.js
+++ b/include/register.js
@@ -5,7 +5,8 @@ module.exports = hexo => {
require('hexo-component-inferno/lib/hexo/filter/locals')(hexo);
require('./hexo/filter/stylus')(hexo);
require('./hexo/generator/category')(hexo);
- require('./hexo/generator/insight')(hexo);
+ require('hexo-component-inferno/lib/hexo/generator/assets')(hexo);
+ require('hexo-component-inferno/lib/hexo/generator/insight')(hexo);
require('hexo-component-inferno/lib/hexo/generator/categories')(hexo);
require('hexo-component-inferno/lib/hexo/generator/tags')(hexo);
require('hexo-component-inferno/lib/hexo/helper/cdn')(hexo);
diff --git a/layout/search/.gitkeep b/layout/search/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/layout/search/algolia.jsx b/layout/search/algolia.jsx
deleted file mode 100644
index 2df359925..000000000
--- a/layout/search/algolia.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-const { Component, Fragment } = require('inferno');
-const { cacheComponent } = require('hexo-component-inferno/lib/util/cache');
-
-class Algolia extends Component {
- render() {
- const {
- translation,
- applicationId,
- apiKey,
- indexName,
- jsUrl,
- algoliaSearchUrl,
- instantSearchUrl
- } = this.props;
-
- if (!applicationId || !apiKey || !indexName) {
- return
- It seems that you forget to set the applicationId
, apiKey
,
- or indexName
for the Aloglia.
- Please set it in _config.yml
.
-
;
- }
-
- const js = `document.addEventListener('DOMContentLoaded', function () {
- loadAlgolia(${JSON.stringify({ applicationId, apiKey, indexName })}, ${JSON.stringify(translation)});
- });`;
-
- return
-
-
-
-
-
- ;
- }
-}
-
-Algolia.Cacheable = cacheComponent(Algolia, 'search.algolia', props => {
- const { config, helper } = props;
- const { algolia } = config;
-
- return {
- translation: {
- hint: helper.__('search.hint'),
- no_result: helper.__('search.no_result'),
- untitled: helper.__('search.untitled'),
- empty_preview: helper.__('search.empty_preview')
- },
- applicationId: algolia ? algolia.applicationID : null,
- apiKey: algolia ? algolia.apiKey : null,
- indexName: algolia ? algolia.indexName : null,
- algoliaSearchUrl: helper.cdn('algoliasearch', '4.0.3', 'dist/algoliasearch-lite.umd.js'),
- instantSearchUrl: helper.cdn('instantsearch.js', '4.3.1', 'dist/instantsearch.production.min.js'),
- jsUrl: helper.url_for('/js/algolia.js')
- };
-});
-
-module.exports = Algolia;
diff --git a/layout/search/insight.jsx b/layout/search/insight.jsx
deleted file mode 100644
index 78540367e..000000000
--- a/layout/search/insight.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-const { Component, Fragment } = require('inferno');
-const { cacheComponent } = require('hexo-component-inferno/lib/util/cache');
-
-class Insight extends Component {
- render() {
- const { translation, contentUrl, jsUrl } = this.props;
-
- const js = `document.addEventListener('DOMContentLoaded', function () {
- loadInsight(${JSON.stringify({ contentUrl })}, ${JSON.stringify(translation)});
- });`;
-
- return
-
-
-
- ;
- }
-}
-
-Insight.Cacheable = cacheComponent(Insight, 'search.insight', props => {
- const { helper } = props;
-
- return {
- translation: {
- hint: helper.__('search.hint'),
- untitled: helper.__('search.untitled'),
- posts: helper._p('common.post', Infinity),
- pages: helper._p('common.page', Infinity),
- categories: helper._p('common.category', Infinity),
- tags: helper._p('common.tag', Infinity)
- },
- contentUrl: helper.url_for('/content.json'),
- jsUrl: helper.url_for('/js/insight.js')
- };
-});
-
-module.exports = Insight;
diff --git a/package.json b/package.json
index def20d5d8..d34c2af70 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"bulma-stylus": "0.8.0",
"deepmerge": "^4.2.2",
"hexo": "^4.2.0",
- "hexo-component-inferno": "^0.1.3",
+ "hexo-component-inferno": "^0.2.0",
"hexo-log": "^1.0.0",
"hexo-pagination": "^1.0.0",
"hexo-renderer-inferno": "^0.1.3",
diff --git a/source/js/algolia.js b/source/js/algolia.js
deleted file mode 100644
index f4719651b..000000000
--- a/source/js/algolia.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/* global instantsearch, algoliasearch */
-function loadAlgolia(config, translation) { // eslint-disable-line no-unused-vars
- const search = instantsearch({
- indexName: config.indexName,
- searchClient: algoliasearch(config.applicationId, config.apiKey)
- });
-
- search.addWidgets([
- instantsearch.widgets.configure({
- attributesToSnippet: ['excerpt']
- })
- ]);
-
- search.addWidget(instantsearch.widgets.searchBox({
- container: '#algolia-input',
- placeholder: translation.hint,
- showReset: false,
- showSubmit: false,
- showLoadingIndicator: false,
- cssClasses: {
- root: 'searchbox-input-container',
- form: 'searchbox-input-container',
- input: 'searchbox-input'
- }
- }));
-
- search.addWidget(instantsearch.widgets.poweredBy({
- container: '#algolia-poweredby'
- }));
-
- search.addWidget(instantsearch.widgets.hits({
- container: '.searchbox-body',
- escapeHTML: false,
- cssClasses: {
- root: 'searchbox-result-container',
- emptyRoot: ['searchbox-result-item', 'disabled']
- },
- templates: {
- empty: function(results) {
- return translation.no_result + ': ' + results.query;
- },
- item: function(hit) {
- const title = instantsearch.highlight({ attribute: 'title', hit });
- let excerpt = instantsearch.highlight({ attribute: 'excerpt', hit });
- excerpt = excerpt.replace(new RegExp('', 'ig'), '[algolia-highlight]')
- .replace(new RegExp('', 'ig'), '[/algolia-highlight]')
- .replace(/(<([^>]+)>)/ig, '')
- .replace(/(\[algolia-highlight\])/ig, '')
- .replace(/(\[\/algolia-highlight\])/ig, '');
- return ``;
- }
- }
- }));
-
- search.addWidget(instantsearch.widgets.pagination({
- container: '.searchbox-footer',
- cssClasses: {
- list: 'searchbox-pagination',
- item: 'searchbox-pagination-item',
- link: 'searchbox-pagination-link',
- selectedItem: 'active',
- disabledItem: 'disabled'
- }
- }));
-
- search.start();
-
- if (location.hash.trim() === '#algolia-search') {
- $('.searchbox').addClass('show');
- }
-
- $(document).on('click', '.navbar-main .search', () => {
- $('.searchbox').toggleClass('show');
- $('.searchbox-input').focus();
- }).on('click', '.searchbox .searchbox-mask', () => {
- $('.searchbox').removeClass('show');
- }).on('click', '.searchbox-close', () => {
- $('.searchbox').removeClass('show');
- });
-}
diff --git a/source/js/insight.js b/source/js/insight.js
deleted file mode 100644
index 33150b327..000000000
--- a/source/js/insight.js
+++ /dev/null
@@ -1,313 +0,0 @@
-/**
- * Insight search plugin
- * @author PPOffice { @link https://github.com/ppoffice }
- */
-function loadInsight(config, translation) { // eslint-disable-line no-unused-vars
- const $main = $('.searchbox');
- const $input = $main.find('.searchbox-input');
- const $container = $main.find('.searchbox-body');
-
- function section(title) {
- return $('').addClass('searchbox-result-section').append($('').text(title));
- }
-
- function merge(ranges) {
- let last;
- const result = [];
-
- ranges.forEach(r => {
- if (!last || r[0] > last[1]) {
- result.push(last = r);
- } else if (r[1] > last[1]) {
- last[1] = r[1];
- }
- });
-
- return result;
- }
-
- function findAndHighlight(text, matches, maxlen) {
- if (!Array.isArray(matches) || !matches.length || !text) {
- return maxlen ? text.slice(0, maxlen) : text;
- }
- const testText = text.toLowerCase();
- const indices = matches.map(match => {
- const index = testText.indexOf(match.toLowerCase());
- if (!match || index === -1) {
- return null;
- }
- return [index, index + match.length];
- }).filter(match => {
- return match !== null;
- }).sort((a, b) => {
- return a[0] - b[0] || a[1] - b[1];
- });
-
- if (!indices.length) {
- return text;
- }
-
- let result = ''; let last = 0;
- const ranges = merge(indices);
- const sumRange = [ranges[0][0], ranges[ranges.length - 1][1]];
- if (maxlen && maxlen < sumRange[1]) {
- last = sumRange[0];
- }
-
- for (let i = 0; i < ranges.length; i++) {
- const range = ranges[i];
- result += text.slice(last, Math.min(range[0], sumRange[0] + maxlen));
- if (maxlen && range[0] >= sumRange[0] + maxlen) {
- break;
- }
- result += '' + text.slice(range[0], range[1]) + '';
- last = range[1];
- if (i === ranges.length - 1) {
- if (maxlen) {
- result += text.slice(range[1], Math.min(text.length, sumRange[0] + maxlen + 1));
- } else {
- result += text.slice(range[1]);
- }
- }
- }
-
- return result;
- }
-
- function searchItem(icon, title, slug, preview, url) {
- title = title != null && title !== '' ? title : translation.untitled;
-
- return `
-
-
-
-
-
- ${title}
- ${slug ? '(' + slug + ')' : ''}
-
- ${preview ? '' + preview + '' : ''}
-
- `;
- }
-
- function sectionFactory(keywords, type, array) {
- let $searchItems;
- if (array.length === 0) return null;
- const sectionTitle = translation[type.toLowerCase()];
- switch (type) {
- case 'POSTS':
- case 'PAGES':
- $searchItems = array.map(item => {
- const title = findAndHighlight(item.title, keywords);
- const text = findAndHighlight(item.text, keywords, 100);
- return searchItem('file', title, null, text, item.link);
- });
- break;
- case 'CATEGORIES':
- case 'TAGS':
- $searchItems = array.map(item => {
- const name = findAndHighlight(item.name, keywords);
- const slug = findAndHighlight(item.slug, keywords);
- return searchItem(type === 'CATEGORIES' ? 'folder' : 'tag', name, slug, null, item.link);
- });
- break;
- default:
- return null;
- }
- return section(sectionTitle).append($searchItems);
- }
-
- function parseKeywords(keywords) {
- return keywords.split(' ').filter(keyword => {
- return !!keyword;
- }).map(keyword => {
- return keyword.toLowerCase();
- });
- }
-
- /**
- * 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) {
- const keywordArray = parseKeywords(keywords);
- const containKeywords = keywordArray.filter(keyword => {
- const containFields = fields.filter(field => {
- if (!Object.prototype.hasOwnProperty.call(obj, field)) {
- return false;
- }
- if (obj[field].toLowerCase().indexOf(keyword) > -1) {
- return true;
- }
- return false;
- });
- 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) {
- let value = 0;
- parseKeywords(keywords).forEach(keyword => {
- const pattern = new RegExp(keyword, 'img'); // Global, Multi-line, Case-insensitive
- fields.forEach((field, index) => {
- if (Object.prototype.hasOwnProperty.call(obj, field)) {
- const 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) {
- const weights = weightFactory(keywords);
- const filters = filterFactory(keywords);
- const posts = json.posts;
- const pages = json.pages;
- const tags = json.tags;
- const categories = json.categories;
- return {
- posts: posts.filter(filters.post).sort((a, b) => { return weights.post(b) - weights.post(a); }).slice(0, 5),
- pages: pages.filter(filters.page).sort((a, b) => { return weights.page(b) - weights.page(a); }).slice(0, 5),
- categories: categories.filter(filters.category).sort((a, b) => { return weights.category(b) - weights.category(a); }).slice(0, 5),
- tags: tags.filter(filters.tag).sort((a, b) => { return weights.tag(b) - weights.tag(a); }).slice(0, 5)
- };
- }
-
- function searchResultToDOM(keywords, searchResult) {
- $container.empty();
- for (const key in searchResult) {
- $container.append(sectionFactory(parseKeywords(keywords),
- key.toUpperCase(), searchResult[key]));
- }
- }
-
- function scrollTo($item) {
- if ($item.length === 0) return;
- const wrapperHeight = $container[0].clientHeight;
- const itemTop = $item.position().top - $container.scrollTop();
- const itemBottom = $item[0].clientHeight + $item.position().top;
- if (itemBottom > wrapperHeight + $container.scrollTop()) {
- $container.scrollTop(itemBottom - $container[0].clientHeight);
- }
- if (itemTop < 0) {
- $container.scrollTop($item.position().top);
- }
- }
-
- function selectItemByDiff(value) {
- const $items = $.makeArray($container.find('.searchbox-result-item'));
- let prevPosition = -1;
- $items.forEach((item, index) => {
- if ($(item).hasClass('active')) {
- prevPosition = index;
-
- }
- });
- const 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('href');
- }
- }
-
- $.getJSON(config.contentUrl, json => {
- if (location.hash.trim() === '#insight-search') {
- $main.addClass('show');
- }
- $input.on('input', function() {
- const keywords = $(this).val();
- searchResultToDOM(keywords, search(json, keywords));
- });
- $input.trigger('input');
- });
-
- let touch = false;
- $(document).on('click focus', '.navbar-main .search', () => {
- $main.addClass('show');
- $main.find('.searchbox-input').focus();
- }).on('click touchend', '.searchbox-result-item', function(e) {
- if (e.type !== 'click' && !touch) {
- return;
- }
- gotoLink($(this));
- touch = false;
- }).on('click touchend', '.searchbox-close', e => {
- if (e.type !== 'click' && !touch) {
- return;
- }
- $('.navbar-main').css('pointer-events', 'none');
- setTimeout(() => {
- $('.navbar-main').css('pointer-events', 'auto');
- }, 400);
- $main.removeClass('show');
- touch = false;
- }).on('keydown', 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('.searchbox-result-item.active').eq(0)); break;
- }
- }).on('touchstart', e => {
- touch = true;
- }).on('touchmove', e => {
- touch = false;
- });
-}