refactor(*): use hexo-component-inferno

This commit is contained in:
ppoffice 2020-01-22 23:07:43 -05:00
parent e3737f5488
commit fe70756366
130 changed files with 75 additions and 3894 deletions

View File

@ -10,8 +10,8 @@ module.exports = hexo => {
const SCHEMA_ROOT = path.join(hexo.theme_dir, 'include/schema/');
const CONFIG_PATH = path.join(hexo.theme_dir, '_config.yml');
const yaml = require('../include/util/yaml');
const { SchemaLoader } = require('../include/util/schema');
const yaml = require('hexo-component-inferno/lib/util/yaml');
const { SchemaLoader } = require('hexo-component-inferno/lib/core/schema');
const loader = SchemaLoader.load(require(path.join(SCHEMA_ROOT, 'config.json')), SCHEMA_ROOT);
const schema = loader.getSchema('/config.json');
logger.info('=== Checking the configuration file ===');
@ -32,7 +32,7 @@ module.exports = hexo => {
let cfg = yaml.parse(cfgStr);
// Check config version
if (!process.argv.includes('--icarus-dont-upgrade-config')) {
const migrator = new(require('../include/util/migrate'))(path.join(hexo.theme_dir, 'include/migration'));
const migrator = new(require('hexo-component-inferno/lib/core/migrate'))(path.join(hexo.theme_dir, 'include/migration'));
// Upgrade config
if (migrator.isOudated(cfg.version)) {
logger.info(`Your configuration file is outdated (${cfg.version} < ${migrator.getLatestVersion()}). `

View File

@ -1,66 +0,0 @@
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
module.exports = hexo => {
const RESERVED_KEYS = {
post: Object.keys(require('hexo/lib/models/post')(hexo).paths),
page: Object.keys(require('hexo/lib/models/page')(hexo).paths)
};
function getThemeConfig(extension) {
if (fs.existsSync(path.join(hexo.theme_dir, '_config' + extension + '.yml'))) {
return yaml.safeLoad(fs.readFileSync(path.join(hexo.theme_dir, '_config' + extension + '.yml')));
}
return null;
}
const ALTERNATIVE_CONFIG = {
post: getThemeConfig('.post'),
page: getThemeConfig('.page')
};
function getExtraConfig(source, reservedKeys) {
const result = {};
for (const key in source) {
if (!key.startsWith('_') && !reservedKeys.includes(key) && typeof source[key] !== 'function') {
result[key] = source[key];
}
}
return result;
}
hexo.extend.filter.register('template_locals', locals => {
// inject helper functions
locals.helper = {};
const helpers = hexo.extend.helper.list();
for (const name in helpers) {
locals.helper[name] = helpers[name].bind(locals);
}
if (typeof locals.__ === 'function') {
locals.helper.__ = locals.__;
}
if (typeof locals._p === 'function') {
locals.helper._p = locals._p;
}
const page = locals.page;
if (page) {
if ((page.layout !== 'page' || page.layout !== 'post') && ALTERNATIVE_CONFIG[page.layout]) {
// load alternative config if exists
locals.config = Object.assign({}, Object.getPrototypeOf(locals).theme || locals.theme, ALTERNATIVE_CONFIG[page.layout]);
} else {
// site config already merged into theme config in hexo/lib/hexo/index.js#Hexo.prototype._generateLocals()
locals.config = Object.assign({}, Object.getPrototypeOf(locals).theme || locals.theme);
}
// merge page configs
if (page.__post === true) {
Object.assign(locals.config, getExtraConfig(page, RESERVED_KEYS.page));
} else if (page.__page === true) {
Object.assign(locals.config, getExtraConfig(page, RESERVED_KEYS.page));
}
}
return locals;
});
};

View File

@ -1,14 +0,0 @@
/**
* Category list page generator
*/
module.exports = function(hexo) {
hexo.extend.generator.register('categories', locals => {
return {
path: 'categories/',
layout: ['categories'],
data: Object.assign({}, locals, {
__categories: true
})
};
});
};

View File

@ -1,14 +0,0 @@
/**
* Tag list page generator
*/
module.exports = function(hexo) {
hexo.extend.generator.register('tags', locals => {
return {
path: 'tags/',
layout: ['tags'],
data: Object.assign({}, locals, {
__tags: true
})
};
});
};

View File

@ -1,99 +0,0 @@
/**
* CDN static file resolvers.
*
* @example
* <%- cdn(package, version, filename) %>
* <%- fontcdn(fontName) %>
* <%- iconcdn() %>
*/
const PROVIDERS = {
LIBRARY: {
cdnjs: '[cdnjs]https://cdnjs.cloudflare.com/ajax/libs/${ package }/${ version }/${ filename }',
loli: '[cdnjs]https://cdnjs.loli.net/ajax/libs/${ package }/${ version }/${ filename }',
jsdelivr: 'https://cdn.jsdelivr.net/npm/${ package }@${ version }/${ filename }',
unpkg: 'https://unpkg.com/${ package }@${ version }/${ filename }'
},
FONT: {
google: 'https://fonts.googleapis.com/${ type }?family=${ fontname }',
loli: 'https://fonts.loli.net/${ type }?family=${ fontname }'
},
ICON: {
fontawesome: 'https://use.fontawesome.com/releases/v5.12.0/css/all.css'
}
};
/**
* Convert npm library path to CDN.js path
*/
const CDNJS_FIXTURES = {
'moment': (ver, fname) => [
'moment.js', ver, fname.startsWith('min/') ? fname.substr(4) : fname
],
'outdatedbrowser': (ver, fname) => [
'outdated-browser', ver, fname.startsWith('outdatedbrowser/') ? fname.substr(16) : fname
],
'highlight.js': (ver, fname) => [
'highlight.js', ver, fname.endsWith('.css') && fname.indexOf('.min.') === -1
? fname.substr(0, fname.length - 4) + '.min.css' : fname
],
'mathjax': (ver, fname) => [
'mathjax', ver, fname.startsWith('unpacked/') ? fname.substr(9) : fname
],
'katex': (ver, fname) => [
'KaTeX', ver, fname
],
'pace-js': (ver, fname) => [
'pace', ver, fname
],
'clipboard': (ver, fname) => [
'clipboard.js', ver, fname
],
// disqusjs is not hosted on CDN.js
'disqusjs': (ver, fname) => []
};
module.exports = function(hexo) {
hexo.extend.helper.register('cdn', function(_package, version, filename) {
let { cdn = 'jsdelivr' } = typeof this.config.providers === 'object' ? this.config.providers : {};
if (cdn in PROVIDERS.LIBRARY) {
cdn = PROVIDERS.LIBRARY[cdn];
}
// cdn.js does not follow a GitHub npm style like jsdeliver and unpkg do. Patch it!
if (cdn === 'cdnjs' || cdn.startsWith('[cdnjs]')) {
if (cdn.startsWith('[cdnjs]')) {
cdn = cdn.substr(7);
}
if (filename.startsWith('dist/')) {
filename = filename.substr(5);
}
if (Object.prototype.hasOwnProperty.call(CDNJS_FIXTURES, _package)) {
[_package, version, filename] = CDNJS_FIXTURES[_package](version, filename);
// package is not hosted on CDN.js
if (!_package) {
cdn = 'jsdelivr';
}
}
}
return cdn.replace(/\${\s*package\s*}/gi, _package)
.replace(/\${\s*version\s*}/gi, version)
.replace(/\${\s*filename\s*}/gi, filename);
});
hexo.extend.helper.register('fontcdn', function(fontName, type = 'css') {
let { fontcdn = 'google' } = typeof this.config.providers === 'object' ? this.config.providers : {};
if (fontcdn in PROVIDERS.FONT) {
fontcdn = PROVIDERS.FONT[fontcdn];
}
return fontcdn.replace(/\${\s*fontname\s*}/gi, fontName)
.replace(/\${\s*type\s*}/gi, type);
});
hexo.extend.helper.register('iconcdn', function() {
let { iconfont = 'fontawesome' } = typeof this.config.providers === 'object' ? this.config.providers : {};
if (iconfont in PROVIDERS.ICON) {
iconfont = PROVIDERS.ICON[iconfont];
}
return iconfont;
});
};

View File

@ -1,16 +0,0 @@
/**
* Helper functions for page/post.
*
* @example
* <%- is_categories(page) %>
* <%- is_tags(page) %>
*/
module.exports = function(hexo) {
hexo.extend.helper.register('is_categories', function(page = null) {
return (page === null ? this.page : page).__categories === true;
});
hexo.extend.helper.register('is_tags', function(page = null) {
return (page === null ? this.page : page).__tags === true;
});
};

View File

@ -1,24 +0,0 @@
/**
* Helper functions for post thumbnail.
*
* @example
* <%- has_thumbnail(post) %>
* <%- get_thumbnail(post) %>
*/
module.exports = function(hexo) {
hexo.extend.helper.register('has_thumbnail', function(post) {
const { article } = this.config;
if (typeof post !== 'object') {
return false;
}
if (article && article.thumbnail === false) {
return false;
}
return 'thumbnail' in post && post.thumbnail;
});
hexo.extend.helper.register('get_thumbnail', function(post) {
const { url_for, has_thumbnail } = this.helper;
return url_for(has_thumbnail.call(this, post) ? post.thumbnail : '/img/thumbnail.svg');
});
};

View File

@ -1,13 +1,13 @@
const logger = require('hexo-log')();
const deepmerge = require('deepmerge');
const Migration = require('../util/migrate').Migration;
const Migration = require('hexo-component-inferno/lib/core/migrate').Migration;
module.exports = class extends Migration {
constructor() {
super('3.0.0', null);
}
doMigrate(config) {
upgrade(config) {
const result = deepmerge({}, config);
result.head = {
favicon: config.favicon || null,

View File

@ -1,15 +1,16 @@
const logger = require('hexo-log')();
module.exports = hexo => {
logger.info('=== Patching Hexo ===');
require('../include/generator/categories')(hexo);
require('../include/generator/category')(hexo);
require('../include/generator/tags')(hexo);
require('../include/generator/insight')(hexo);
require('../include/filter/locals')(hexo);
require('../include/helper/cdn')(hexo);
require('../include/helper/page')(hexo);
require('../include/helper/thumbnail')(hexo);
logger.info('=== Registering Hexo extensions ===');
require('hexo-component-inferno/lib/hexo/filter/locals')(hexo);
require('./generator/category')(hexo);
require('./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);
require('hexo-component-inferno/lib/hexo/helper/page')(hexo);
require('hexo-component-inferno/lib/hexo/helper/thumbnail')(hexo);
require('hexo-component-inferno/lib/core/view').init(hexo);
const hooks = [
'after_render:html',

View File

View File

@ -1,25 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/changyan.json",
"description": "Changyan comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "changyan"
},
"app_id": {
"type": "string",
"description": "Changyan app ID"
},
"conf": {
"type": "string",
"description": "Changyan configuration ID"
}
},
"required": [
"type",
"app_id",
"conf"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/disqus.json",
"description": "Disqus comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "disqus"
},
"shortname": {
"type": "string",
"description": "Disqus shortname"
}
},
"required": [
"type",
"shortname"
]
}

View File

@ -1,58 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/disqusjs.json",
"description": "DisqusJS comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "disqusjs"
},
"shortname": {
"type": "string",
"description": "Disqus shortname"
},
"api_key": {
"description": "Disqus application API key",
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": [
{
"type": "string"
}
]
}
]
},
"api": {
"type": "string",
"description": "Disqus API endpoint"
},
"admin": {
"type": "string",
"description": "Disqus moderator username",
"nullable": true
},
"admin_label": {
"type": "string",
"description": "Disqus moderator badge text",
"default": false,
"nullable": true
},
"nesting": {
"type": "integer",
"description": "Maximum number of comment nesting level",
"default": 4,
"nullable": true
}
},
"required": [
"type",
"shortname",
"api_key"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/facebook.json",
"description": "Facebook comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "facebook"
}
},
"required": [
"type"
]
}

View File

@ -1,83 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/gitalk.json",
"description": "Gitalk comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "gitalk"
},
"client_id": {
"type": "string",
"description": "GitHub application client ID"
},
"client_secret": {
"type": "string",
"description": "GitHub application client secret"
},
"repo": {
"type": "string",
"description": "GitHub repository"
},
"owner": {
"type": "string",
"description": "GitHub repository owner. Can be personal user or organization"
},
"admin": {
"type": "array",
"description": "GitHub repository owner and collaborators. (Users who having write access to this repository)",
"items": {
"type": "string"
}
},
"per_page": {
"type": "number",
"description": "Pagination size, with maximum 100",
"default": 10,
"nullable": true
},
"distraction_free_mode": {
"type": "boolean",
"description": "Facebook-like distraction free mode",
"default": false,
"nullable": true
},
"pager_direction": {
"type": "string",
"description": "Comment sorting direction, available values are `last` and `first`",
"default": "last",
"nullable": true
},
"create_issue_manually": {
"type": "boolean",
"description": "Create GitHub issues manually for each page",
"default": false,
"nullable": true
},
"proxy": {
"type": "string",
"description": "GitHub oauth request reverse proxy for CORS",
"nullable": true
},
"flip_move_options": {
"type": "object",
"description": "Comment list animation",
"nullable": true
},
"enable_hotkey": {
"type": "boolean",
"description": "Enable hot key (cmd|ctrl + enter) submit comment",
"default": true,
"nullable": true
}
},
"required": [
"type",
"client_id",
"client_secret",
"repo",
"owner",
"admin"
]
}

View File

@ -1,53 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/gitment.json",
"description": "Gitment comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "gitment"
},
"owner": {
"type": "string",
"description": "Your GitHub ID"
},
"repo": {
"type": "string",
"description": "The repository to store your comments. Make sure you're repo's owner"
},
"client_id": {
"type": "string",
"description": "GitHub client ID"
},
"client_secret": {
"type": "string",
"description": "GitHub client secret"
},
"theme": {
"type": "string",
"description": "An optional Gitment theme object",
"default": "gitment.defaultTheme",
"nullable": true
},
"per_page": {
"type": "number",
"description": "An optional number to which comments will be paginated",
"default": 20,
"nullable": true
},
"max_comment_height": {
"type": "number",
"description": "An optional number to limit comments' max height, over which comments will be folded",
"default": 250,
"nullable": true
}
},
"required": [
"type",
"owner",
"repo",
"client_id",
"client_secret"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/isso.json",
"description": "Isso comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "isso"
},
"url": {
"type": "string",
"description": "URL to your Isso comment service"
}
},
"required": [
"type",
"url"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/livere.json",
"description": "Livere comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "livere"
},
"uid": {
"type": "string",
"description": "LiveRe comment service UID"
}
},
"required": [
"type",
"uid"
]
}

View File

@ -1,102 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/comment/valine.json",
"description": "Valine comment plugin configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "valine"
},
"app_id": {
"type": "string",
"description": "Application <APP_ID> from Leancloud"
},
"app_key": {
"type": "string",
"description": "Application <APP_KEY> from Leancloud"
},
"placeholder": {
"type": "string",
"description": "Comment box placeholders",
"nullable": true
},
"notify": {
"type": "boolean",
"description": "Enable email notification when someone comments",
"default": false,
"nullable": true
},
"verify": {
"type": "boolean",
"description": "Enable verification code service",
"default": false,
"nullable": true
},
"avatar": {
"type": "string",
"description": "Gravatar type",
"enum": [
"",
"mp",
"identicon",
"monsterid",
"wavatar",
"robohash",
"retro",
"hide",
"mm"
],
"default": "mm",
"nullable": true
},
"avatar_force": {
"type": "boolean",
"description": "Pull the latest avatar upon page visit",
"default": false,
"nullable": true
},
"meta": {
"type": "array",
"description": "Reviewer attributes",
"items": {
"type": "string"
},
"default": [
"nick",
"mail",
"link"
],
"nullable": true
},
"page_size": {
"type": "integer",
"description": "Number of comments per page",
"default": 10,
"nullable": true
},
"visitor": {
"type": "boolean",
"description": "Show visitor count",
"default": false,
"nullable": true
},
"highlight": {
"type": "boolean",
"description": "Enable code highlighting",
"default": true,
"nullable": true
},
"record_ip": {
"type": "boolean",
"description": "Record reviewer IP address",
"default": false,
"nullable": true
}
},
"required": [
"type",
"app_id",
"app_key"
]
}

View File

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/donate/alipay.json",
"description": "Alipay donate button configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "alipay"
},
"qrcode": {
"type": "string",
"description": "Alipay qrcode image URL"
}
},
"required": [
"type",
"qrcode"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/donate/buymeacoffee.json",
"description": "\"Buy me a coffee\" donate button configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "buymeacoffee"
},
"url": {
"type": "string",
"description": "URL to the \"Buy me a coffee\" page"
}
},
"required": [
"type",
"url"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/donate/patreon.json",
"description": "Patreon donate button configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "patreon"
},
"url": {
"type": "string",
"description": "URL to the Patreon page"
}
},
"required": [
"type",
"url"
]
}

View File

@ -1,28 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/donate/paypal.json",
"description": "Paypal donate button configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "paypal"
},
"business": {
"type": "string",
"description": "Paypal business ID or email address"
},
"currency_code": {
"type": "string",
"description": "Currency code",
"examples": [
"USD"
]
}
},
"required": [
"type",
"business",
"currency_code"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/donate/wechat.json",
"description": "Wechat donate button configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "wechat"
},
"qrcode": {
"type": "string",
"description": "Wechat qrcode image URL"
}
},
"required": [
"type",
"qrcode"
]
}

View File

View File

@ -1,11 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/misc/meta.json",
"description": "Additional HTML meta tags in an array",
"type": "array",
"items": {
"type": "string",
"description": "Meta tag specified in <attribute>=<value> style\nE.g., name=theme-color;content=#123456 => <meta name=\"theme-color\" content=\"#123456\">"
},
"nullable": true
}

View File

@ -1,81 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/misc/open_graph.json",
"description": "Open Graph metadata\nhttps://hexo.io/docs/helpers.html#open-graph",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Page title (og:title) (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"type": {
"type": "string",
"description": "Page type (og:type) (optional)\nYou should leave this blank for most of the time",
"default": "blog",
"nullable": true
},
"url": {
"type": "string",
"description": "Page URL (og:url) (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"image": {
"type": [
"string",
"array"
],
"description": "Page cover (og:image) (optional) Default to the Open Graph image or thumbnail of the page\nYou should leave this blank for most of the time",
"items": {
"type": "string"
},
"nullable": true
},
"site_name": {
"type": "string",
"description": "Site name (og:site_name) (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"author": {
"type": "string",
"description": "Page author (article:author) (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"description": {
"type": "string",
"description": "Page description (og:description) (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"twitter_card": {
"type": "string",
"description": "Twitter card type (twitter:card)",
"nullable": true
},
"twitter_id": {
"type": "string",
"description": "Twitter ID (twitter:creator)",
"nullable": true
},
"twitter_site": {
"type": "string",
"description": "Twitter ID (twitter:creator)",
"nullable": true
},
"google_plus": {
"type": "string",
"description": "Google+ profile link (deprecated)",
"nullable": true
},
"fb_admins": {
"type": "string",
"description": "Facebook admin ID",
"nullable": true
},
"fb_app_id": {
"type": "string",
"description": "Facebook App ID",
"nullable": true
}
},
"nullable": true
}

View File

@ -1,41 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/misc/poly_links.json",
"description": "A polymorphic link",
"type": "object",
"patternProperties": {
".+": {
"type": [
"string",
"object"
],
"description": "URL or path of the link, with/without the icon element class name",
"properties": {
"url": {
"type": "string",
"description": "URL or path of the link"
},
"icon": {
"type": "string",
"description": "Icon element class name"
}
},
"required": [
"url",
"icon"
]
}
},
"examples": [
{
"My GitHub Page": "https://github.com/ppoffice"
},
{
"My GitHub Page": {
"url": "https://github.com/ppoffice",
"icon": "fab fa-github"
}
}
],
"nullable": true
}

View File

@ -1,40 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/misc/structured_data.json",
"description": "Structured data of the page\nhttps://developers.google.com/search/docs/guides/intro-structured-data",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Page title (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"description": {
"type": "string",
"description": "Page description (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"url": {
"type": "string",
"description": "Page URL (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"author": {
"type": "string",
"description": "Page author (article:author) (optional)\nYou should leave this blank for most of the time",
"nullable": true
},
"image": {
"type": [
"string",
"array"
],
"description": "Page images (optional) Default to the Open Graph image or thumbnail of the page\nYou should leave this blank for most of the time",
"items": {
"type": "string"
},
"nullable": true
}
},
"nullable": true
}

View File

@ -1,16 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/baidu_analytics.json",
"description": "Baidu Analytics plugin settings\nhttps://tongji.baidu.com",
"type": "object",
"properties": {
"tracking_id": {
"type": "string",
"description": "Baidu Analytics tracking ID",
"nullable": true
}
},
"required": [
"tracking_id"
]
}

View File

@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/busuanzi.json",
"description": "BuSuanZi site/page view counter\nhttps://busuanzi.ibruce.info",
"type": "boolean",
"default": false
}

View File

@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/gallery.json",
"description": "Enable the lightGallery and Justified Gallery plugins\nhttps://ppoffice.github.io/hexo-theme-icarus/Plugins/General/gallery-plugin/",
"type": "boolean",
"default": true
}

View File

@ -1,16 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/google_analytics.json",
"description": "Google Analytics plugin settings\nhttps://analytics.google.com",
"type": "object",
"properties": {
"tracking_id": {
"type": "string",
"description": "Google Analytics tracking ID",
"nullable": true
}
},
"required": [
"tracking_id"
]
}

View File

@ -1,19 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/hotjar.json",
"description": "Hotjar user feedback plugin\nhttps://www.hotjar.com/",
"type": "object",
"properties": {
"site_id": {
"type": [
"string",
"number"
],
"description": "Hotjar site id",
"nullable": true
}
},
"required": [
"site_id"
]
}

View File

@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/katex.json",
"description": "Enable the KaTeX math typesetting supprot\nhttps://katex.org/",
"type": "boolean",
"default": false
}

View File

@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/mathjax.json",
"description": "Enable the MathJax math typesetting support\nhttps://www.mathjax.org/",
"type": "boolean",
"default": false
}

View File

@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/outdated_browser.json",
"description": "Enable the Outdated Browser plugin\nhttp://outdatedbrowser.com/",
"type": "boolean",
"default": false
}

View File

@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/plugin/progressbar.json",
"description": "Show a progress bar at top of the page on page loading",
"type": "boolean",
"default": true
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/search/baidu.json",
"description": "Enable Baidu search",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "baidu"
}
},
"required": [
"type"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/search/google_cse.json",
"description": "Enable Google CSE\nhttps://cse.google.com/cse/create/new",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "google_cse"
},
"cx": {
"type": "string",
"description": "Google CSE cx value"
}
},
"required": [
"type",
"cx"
]
}

View File

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/share/addthis.json",
"description": "Enable AddThis share buttons\nhttps://www.addthis.com/",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "addthis"
},
"install_url": {
"type": "string",
"description": "URL to the AddThis share plugin script"
}
},
"required": [
"type",
"install_url"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/share/addtoany.json",
"description": "Enable AddToAny share buttons\nhttps://www.addtoany.com/",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "addtoany"
}
},
"required": [
"type"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/share/bdshare.json",
"description": "Enable Baidu share buttons",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "bdshare"
}
},
"required": [
"type"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/share/sharejs.json",
"description": "Enable Share.js share buttons\nhttps://github.com/overtrue/share.js/",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "sharejs"
}
},
"required": [
"type"
]
}

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/share/sharethis.json",
"description": "Enable ShareThis share buttons\nhttps://sharethis.com/",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "sharethis"
},
"install_url": {
"type": "string",
"description": "URL to the ShareThis share plugin script"
}
},
"required": [
"type",
"install_url"
]
}

View File

@ -1,25 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/adsense.json",
"description": "Google AdSense unit configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "adsense"
},
"client_id": {
"type": "string",
"description": "AdSense client ID"
},
"slot_id": {
"type": "string",
"description": "AdSense AD unit ID"
}
},
"required": [
"type",
"client_id",
"slot_id"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/archives.json",
"description": "Archives widget configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "archives"
}
},
"required": [
"type"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/categories.json",
"description": "Categories widget configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "categories"
}
},
"required": [
"type"
]
}

View File

@ -1,32 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/links.json",
"description": "Recommendation links widget configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "links"
},
"links": {
"type": "object",
"description": "Names and URLs of the sites",
"patternProperties": {
".+": {
"type": "string",
"description": "URL of the site"
}
},
"examples": [
{
"Hexo": "https://hexo.io",
"Bulma": "https://bulma.io"
}
],
"nullable": true
}
},
"required": [
"type"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/recent_posts.json",
"description": "Recent posts widget configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "recent_posts"
}
},
"required": [
"type"
]
}

View File

@ -1,25 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/subscribe_email.json",
"description": "Google FeedBurner email subscription widget configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "subscribe_email"
},
"description": {
"type": "string",
"description": "Hint text under the email input",
"nullable": true
},
"feedburner_id": {
"type": "string",
"description": "Feedburner ID"
}
},
"required": [
"type",
"feedburner_id"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/tags.json",
"description": "Tags widget configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "tags"
}
},
"required": [
"type"
]
}

View File

@ -1,15 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/widget/toc.json",
"description": "Table of contents widget configurations",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "toc"
}
},
"required": [
"type"
]
}

View File

@ -1,110 +0,0 @@
const path = require('path');
const logger = require('hexo-log')();
class Version {
constructor(version) {
const ver = version.split('.').map(i => parseInt(i, 10));
if (ver.length !== 3) {
throw new Error('Malformed version number ' + version);
}
this.major = ver[0];
this.minor = ver[1];
this.patch = ver[2];
}
toString() {
return `${this.major}.${this.minor}.${this.patch}`;
}
}
Version.compare = function(a, b) {
if (!(a instanceof Version) || !(b instanceof Version)) {
throw new Error('Cannot compare non-Versions');
}
if (a.major !== b.major) {
return a.major - b.major;
}
if (a.minor !== b.minor) {
return a.minor - b.minor;
}
if (a.patch !== b.patch) {
return a.patch - b.patch;
}
return 0;
};
class Migration {
/**
* @param {string} version Target version
* @param {string} head File name of the previous migration
*/
constructor(version, head) {
this.version = new Version(version);
this.head = head;
}
doMigrate(config) {
throw new Error('Not implemented!');
}
migrate(config) {
logger.info(`Updating configurations from ${config.version} to ${this.version.toString()}...`);
const result = this.doMigrate(config);
result.version = this.version.toString();
return result;
}
}
class Migrator {
constructor(root) {
this.versions = [];
this.migrations = {};
let head = 'head';
while (head) {
const migration = new(require(path.join(root, head)))();
if (!(migration instanceof Migration)) {
throw new Error(`Migration ${head} is not a Migration class.`);
}
this.versions.push(migration.version);
this.migrations[migration.version.toString()] = migration;
head = migration.head;
}
this.versions.sort(Version.compare);
}
isOudated(version) {
if (!this.versions.length) {
return false;
}
return Version.compare(new Version(version), this.getLatestVersion()) < 0;
}
getLatestVersion() {
if (!this.versions.length) {
return null;
}
return this.versions[this.versions.length - 1];
}
migrate(config, toVersion = null) {
const fVer = new Version(config.version);
const tVer = toVersion ? new Version(toVersion) : this.getLatestVersion();
// find all migrations whose version is larger than fromVer, smaller or equal to toVer
// and run migrations on the config one by one
return this.versions.filter(ver => Version.compare(ver, fVer) > 0 && Version.compare(ver, tVer) <= 0)
.sort(Version.compare)
.reduce((cfg, ver) => {
const migration = this.migrations[ver.toString()];
return migration.migrate(cfg);
}, config);
}
}
Migrator.Version = Version;
Migrator.Migration = Migration;
module.exports = Migrator;

View File

@ -1,305 +0,0 @@
const Ajv = require('ajv');
const path = require('path');
const deepmerge = require('deepmerge');
const yaml = require('./yaml');
const MAGIC = 'c823d4d4';
const PRIMITIVE_DEFAULTS = {
'null': null,
'boolean': false,
'number': 0,
'integer': 0,
'string': '',
'array': [],
'object': {}
};
class DefaultValue {
constructor(value, description) {
this.value = value;
this.description = description;
}
merge(source) {
if ('description' in source && source.description) {
this.description = source.description;
}
if ('value' in source && source.value) {
if (this.value instanceof DefaultValue) {
this.value.merge(source.value);
} else if (Array.isArray(this.value) && Array.isArray(source.value)) {
this.value.concat(...source.value);
} else if (typeof this.value === 'object' && typeof source.value === 'object') {
for (const key in source.value) {
this.value[key] = source.value[key];
}
} else {
this.value = deepmerge(this.value, source.value);
}
}
return this;
}
clone() {
const result = new DefaultValue(this.value, this.description);
if (result.value instanceof DefaultValue) {
result.value = result.value.clone();
} else if (Array.isArray(result.value)) {
result.value = [].concat(...result.value);
} else if (typeof result.value === 'object') {
result.value = Object.assign({}, result.value);
}
return result;
}
toCommentedArray() {
return [].concat(...this.value.map(item => {
if (item instanceof DefaultValue) {
if (typeof item.description !== 'string' || !item.description.trim()) {
return [item.toCommented()];
}
return item.description.split('\n').map((line, i) => {
return MAGIC + i + ': ' + line;
}).concat(item.toCommented());
}
return [item];
}));
}
toCommentedObject() {
if (this.value instanceof DefaultValue) {
return this.value.toCommented();
}
const result = {};
for (const key in this.value) {
const item = this.value[key];
if (item instanceof DefaultValue) {
if (typeof item.description === 'string' && item.description.trim()) {
item.description.split('\n').forEach((line, i) => {
result[MAGIC + key + i] = line;
});
}
result[key] = item.toCommented();
} else {
result[key] = item;
}
}
return result;
}
toCommented() {
if (Array.isArray(this.value)) {
return this.toCommentedArray();
} else if (typeof this.value === 'object' && this.value !== null) {
return this.toCommentedObject();
}
return this.value;
}
toYaml() {
const regex = new RegExp('^(\\s*)(?:-\\s*\\\')?' + MAGIC + '.*?:\\s*\\\'?(.*?)\\\'*$', 'mg');
return yaml.stringify(this.toCommented()).replace(regex, '$1# $2');// restore comments
}
}
/* eslint-disable no-use-before-define */
class Schema {
constructor(loader, def) {
if (!(loader instanceof SchemaLoader)) {
throw new Error('loader must be an instance of SchemaLoader');
}
if (typeof def !== 'object') {
throw new Error('schema definition must be an object');
}
this.loader = loader;
this.def = def;
this.compiledSchema = null;
}
validate(obj) {
if (!this.compiledSchema) {
this.compiledSchema = this.loader.compileValidator(this.def.$id);
}
return this.compiledSchema(obj) ? true : this.compiledSchema.errors;
}
getArrayDefaultValue(def) {
let value;
const defaultValue = new DefaultValue(null, def.description);
if ('items' in def && typeof def.items === 'object') {
const items = Object.assign({}, def.items);
delete items.oneOf;
value = this.getDefaultValue(items);
}
if ('oneOf' in def.items && Array.isArray(def.items.oneOf)) {
defaultValue.value = def.items.oneOf.map(one => {
if (!(value instanceof DefaultValue)) {
return this.getDefaultValue(one);
}
return value.clone().merge(this.getDefaultValue(one));
});
} else {
if (!Array.isArray(value)) {
value = [value];
}
defaultValue.value = value;
}
return defaultValue;
}
getObjectDefaultValue(def) {
const value = {};
if ('properties' in def && typeof def.properties === 'object') {
for (const property in def.properties) {
value[property] = this.getDefaultValue(def.properties[property]);
}
}
const defaultValue = new DefaultValue(value, def.description);
if ('oneOf' in def && Array.isArray(def.oneOf) && def.oneOf.length) {
defaultValue.merge(this.getDefaultValue(def.oneOf[0]));
defaultValue.description = def.description;
}
return defaultValue;
}
getTypedDefaultValue(def) {
let defaultValue;
const type = Array.isArray(def.type) ? def.type[0] : def.type;
if (type === 'array') {
defaultValue = this.getArrayDefaultValue(def);
} else if (type === 'object') {
defaultValue = this.getObjectDefaultValue(def);
} else if (type in PRIMITIVE_DEFAULTS) {
if ('nullable' in def && def.nullable) {
defaultValue = new DefaultValue(null, def.description);
} else {
defaultValue = new DefaultValue(PRIMITIVE_DEFAULTS[type], def.description);
}
} else {
throw new Error(`Cannot get default value for type ${type}`);
}
// referred default value always get overwritten by its parent default value
if ('$ref' in def && def.$ref) {
defaultValue = this.getReferredDefaultValue(def).merge(defaultValue);
}
return defaultValue;
}
getReferredDefaultValue(def) {
const schema = this.loader.getSchema(def.$ref);
if (!schema) {
throw new Error(`Schema ${def.$ref} is not loaded`);
}
return this.getDefaultValue(schema.def).merge({ description: def.description });
}
getDefaultValue(def = null) {
if (!def) {
def = this.def;
}
if ('const' in def) {
return new DefaultValue(def.const, def.description);
}
if ('default' in def) {
return new DefaultValue(def.default, def.description);
}
if ('examples' in def && Array.isArray(def.examples) && def.examples.length) {
return new DefaultValue(def.examples[0], def.description);
}
if ('type' in def && def.type) {
return this.getTypedDefaultValue(def);
}
// $ref only schemas
if ('$ref' in def && def.$ref) {
return this.getReferredDefaultValue(def);
}
}
}
class SchemaLoader {
constructor() {
this.schemas = {};
this.ajv = new Ajv({ nullable: true });
}
getSchema($id) {
return this.schemas[$id];
}
addSchema(def) {
if (!Object.prototype.hasOwnProperty.call(def, '$id')) {
throw new Error('The schema definition does not have an $id field');
}
this.ajv.addSchema(def);
this.schemas[def.$id] = new Schema(this, def);
}
removeSchema($id) {
this.ajv.removeSchema($id);
delete this.schemas[$id];
}
compileValidator($id) {
return this.ajv.compile(this.schemas[$id].def);
}
}
function traverseObj(obj, targetKey, handler) {
if (Array.isArray(obj)) {
for (const child of obj) {
traverseObj(child, targetKey, handler);
}
} else if (typeof obj === 'object') {
for (const key in obj) {
if (key === targetKey) {
handler(obj[key]);
} else {
traverseObj(obj[key], targetKey, handler);
}
}
}
}
SchemaLoader.load = (rootSchemaDef, resolveDirs = []) => {
if (!Array.isArray(resolveDirs)) {
resolveDirs = [resolveDirs];
}
const loader = new SchemaLoader();
loader.addSchema(rootSchemaDef);
function handler($ref) {
if (typeof $ref !== 'string') {
throw new Error('Invalid schema reference id: ' + JSON.stringify($ref));
}
if (loader.getSchema($ref)) {
return;
}
for (const dir of resolveDirs) {
let def;
try {
def = require(path.join(dir, $ref));
} catch (e) {
continue;
}
if (typeof def !== 'object' || def.$id !== $ref) {
continue;
}
loader.addSchema(def);
traverseObj(def, '$ref', handler);
return;
}
throw new Error('Cannot find schema definition ' + $ref + '.\n'
+ 'Please check if the file exists and its $id is correct');
}
traverseObj(rootSchemaDef, '$ref', handler);
return loader;
};
module.exports = {
Schema,
SchemaLoader,
DefaultValue
};

View File

@ -1,43 +0,0 @@
const yaml = require('js-yaml');
const YamlType = require('js-yaml/lib/js-yaml/type');
const YamlSchema = require('js-yaml/lib/js-yaml/schema');
// output null as empty in yaml
const YAML_SCHEMA = new YamlSchema({
include: [
require('js-yaml/lib/js-yaml/schema/default_full')
],
implicit: [
new YamlType('tag:yaml.org,2002:null', {
kind: 'scalar',
resolve(data) {
if (data === null) {
return true;
}
const max = data.length;
return (max === 1 && data === '~')
|| (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'));
},
construct: () => null,
predicate: object => object === null,
represent: {
empty: () => ''
},
defaultStyle: 'empty'
})
]
});
module.exports = {
parse(str) {
return yaml.safeLoad(str);
},
stringify(object) {
return yaml.safeDump(object, {
indent: 4,
lineWidth: 1024,
schema: YAML_SCHEMA
});
}
};

View File

@ -1,7 +1,7 @@
const moment = require('moment');
const { Component, Fragment } = require('inferno');
const Paginator = require('./misc/paginator');
const ArticleMedia = require('./common/article-media');
const Paginator = require('hexo-component-inferno/lib/view/misc/paginator');
const ArticleMedia = require('hexo-component-inferno/lib/view/common/article-media');
module.exports = class extends Component {
render() {

View File

@ -1,5 +1,5 @@
const { Component } = require('inferno');
const Categories = require('./widget/categories');
const Categories = require('hexo-component-inferno/lib/view/widget/categories');
module.exports = class extends Component {
render() {

0
layout/comment/.gitkeep Normal file
View File

View File

@ -1,30 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class ChangeYan extends Component {
render() {
const { appId, conf, path } = this.props;
if (!appId || !conf) {
return <div class="notification is-danger">
You forgot to set the <code>app_id</code> or <code>conf</code> for Changyan.
Please set it in <code>_config.yml</code>.
</div>;
}
const js = `window.changyan.api.config({appid: '${appId}',conf: '${conf}'});`;
return <Fragment>
<div id="SOHUCS" sid={path}></div>
<script charset="utf-8" src="https://changyan.sohu.com/upload/changyan.js"></script>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(ChangeYan, 'comment.changyan', props => {
const { comment, page } = props;
return {
appId: comment.app_id,
conf: comment.conf,
path: page.path
};
});

View File

@ -1,41 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Disqus extends Component {
render() {
const { shortname, disqusId, path, permalink } = this.props;
if (!shortname) {
return <div class="notification is-danger">
You forgot to set the <code>shortname</code> for Disqus.
Please set it in <code>_config.yml</code>.
</div>;
}
const js = `var disqus_config = function () {
this.page.url = '${permalink}';
this.page.identifier = '${disqusId || path}';
};
(function() {
var d = document, s = d.createElement('script');
s.src = '//' + '${shortname}' + '.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();`;
return <Fragment>
<div id="disqus_thread">
<noscript>Please enable JavaScript to view the <a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Disqus, 'comment.disqus', props => {
const { comment, page } = props;
return {
path: page.path,
shortname: comment.shortname,
disqusId: page.disqusId,
permalink: page.permalink
};
});

View File

@ -1,68 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class DisqusJs extends Component {
render() {
const {
shortname,
apiKey,
api,
admin,
adminLabel = false,
nesting = 4,
disqusId,
path,
permalink,
pageTitle,
siteTitle,
jsUrl,
cssUrl
} = this.props;
if (!shortname) {
return <div class="notification is-danger">
You forgot to set the <code>shortname</code> or <code>api_key</code> for Disqus.
Please set it in <code>_config.yml</code>.
</div>;
}
const js = `new DisqusJS({
shortname: '${shortname}',
apikey: '${JSON.stringify(apiKey)}',
siteName: '${siteTitle}',
identifier: '${disqusId || path}',
url: '${permalink || path}',
title: '${pageTitle}',
api: '${api}',
admin: '${admin}',
adminLabel: '${adminLabel}',
nesting: ${nesting}
});`;
return <Fragment>
<link rel="stylesheet" href={cssUrl} />
<div id="disqus_thread">
<noscript>Please enable JavaScript to view the <a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
<script src={jsUrl}></script>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(DisqusJs, 'comment.disqusjs', props => {
const { config, page, helper, comment } = props;
return {
path: page.path,
shortname: comment.shortname,
apiKey: comment.api_key,
api: comment.api,
admin: comment.admin,
adminLabel: comment.admin_label,
nesting: comment.nesting,
disqusId: page.disqusId,
permalink: page.permalink,
pageTitle: page.title,
siteTitle: config.title,
jsUrl: helper.cdn('disqusjs', '1.2.5', 'dist/disqus.js'),
cssUrl: helper.cdn('disqusjs', '1.2.5', 'dist/disqusjs.css')
};
});

View File

@ -1,28 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Facebook extends Component {
render() {
const { language, permalink } = this.props;
const js = `(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/${language.split('-').join('_')}/sdk.js#xfbml=1&version=v2.8";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));`;
return <Fragment>
<div class="fb-comments" data-width="100%" data-href={permalink} data-num-posts="5"></div>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Facebook, 'comment.facebook', props => {
const { config, page } = props;
return {
language: page.lang || page.language || config.language || 'en',
permalink: page.permalink
};
});

View File

@ -1,79 +0,0 @@
const crypto = require('crypto');
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Gitalk extends Component {
render() {
const {
id,
repo,
owner,
admin,
clientId,
clientSecret,
createIssueManually = false,
distractionFreeMode = false,
pagerDirection = 'last',
perPage = 10,
proxy,
flipMoveOptions,
enableHotKey,
jsUrl,
cssUrl
} = this.props;
if (!id || !repo || !owner || !admin || !clientId || !clientSecret) {
return <div class="notification is-danger">
You forgot to set the <code>owner</code>, <code>admin</code>, <code>repo</code>,
<code>client_id</code>, or <code>client_secret</code> for Gitalk.
Please set it in <code>_config.yml</code>.
</div>;
}
const js = `var gitalk = new Gitalk({
id: '${id}',
repo: '${repo}',
owner: '${owner}',
clientID: '${clientId}',
clientSecret: '${clientSecret}',
admin: ${JSON.stringify(admin)},
createIssueManually: ${createIssueManually},
distractionFreeMode: ${distractionFreeMode},
perPage: ${perPage},
pagerDirection: '${pagerDirection}',
${proxy ? `proxy: '${proxy}',` : ''}
${flipMoveOptions ? `flipMoveOptions: ${JSON.stringify(flipMoveOptions)},` : ''}
enableHotKey: ${enableHotKey ? !!enableHotKey : true}
})
gitalk.render('comment-container')`;
return <Fragment>
<div id="comment-container"></div>
<link rel="stylesheet" href={cssUrl} />
<script src={jsUrl}></script>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Gitalk, 'comment.gitalk', props => {
const { helper, comment } = props;
// FIXME: config name change
const id = crypto.createHash('md5').update(props.page.path).digest('hex');
return {
id,
repo: comment.repo,
owner: comment.owner,
admin: comment.admin,
clientId: comment.client_id,
clientSecret: comment.client_secret,
createIssueManually: comment.create_issue_manually,
distractionFreeMode: comment.distraction_free_mode,
pagerDirection: comment.pager_direction,
perPage: comment.per_page,
proxy: comment.proxy,
flipMoveOptions: comment.flip_move_options,
enableHotKey: comment.enable_hotkey,
cssUrl: helper.cdn('gitalk', '1.4.1', 'dist/gitalk.css'),
jsUrl: helper.cdn('gitalk', '1.4.1', 'dist/gitalk.min.js')
};
});

View File

@ -1,58 +0,0 @@
const crypto = require('crypto');
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Gitment extends Component {
render() {
const {
id,
repo,
owner,
clientId,
clientSecret,
perPage = 20,
maxCommentHeight = 250
} = this.props;
if (!id || !repo || !owner || !clientId || !clientSecret) {
return <div class="notification is-danger">
You forgot to set the <code>owner</code>, <code>repo</code>, <code>clientId</code>,
or <code>clientSecret</code> for Gitment.
Please set it in <code>_config.yml</code>.
</div>;
}
const js = `var gitment = new Gitment({
id: '${id}',
repo: '${repo}',
owner: '${owner}',
oauth: {
client_id: '${clientId}',
client_secret: '${clientSecret}',
},
perPage: ${perPage},
maxCommentHeight: ${maxCommentHeight}
})
gitment.render('comment-container')`;
return <Fragment>
<div id="comment-container"></div>
<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css" />
<script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Gitment, 'comment.gitment', props => {
const { comment } = props;
const id = crypto.createHash('md5').update(props.page.path).digest('hex');
return {
id,
repo: comment.repo,
owner: comment.owner,
clientId: comment.client_id,
clientSecret: comment.client_secret,
perPage: comment.per_page,
maxCommentHeight: comment.max_comment_height
};
});

View File

@ -1,26 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Isso extends Component {
render() {
const { url } = this.props;
if (!url) {
return <div class="notification is-danger">
You forgot to set the <code>url</code> for Isso.
Please set it in <code>_config.yml</code>.
</div>;
}
return <Fragment>
<div id="isso-thread"></div>
<script data-isso={'//' + url} src={`//${url}/js/embed.min.js`}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Isso, 'comment.isso', props => {
const { comment } = props;
return {
url: comment.url
};
});

View File

@ -1,37 +0,0 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
class LiveRe extends Component {
render() {
const { uid } = this.props;
if (!uid) {
return <div class="notification is-danger">
You forgot to set the <code>uid</code> for LiveRe.
Please set it in <code>_config.yml</code>.
</div>;
}
const js = `(function(d, s) {
var j, e = d.getElementsByTagName(s)[0];
if (typeof LivereTower === 'function') { return; }
j = d.createElement(s);
j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
j.async = true;
e.parentNode.insertBefore(j, e);
})(document, 'script');`;
return <div id="lv-container" data-id="city" data-uid={uid}>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
<noscript>Please activate JavaScript for write a comment in LiveRe</noscript>
</div>;
}
}
module.exports = cacheComponent(LiveRe, 'comment.livere', props => {
const { comment } = props;
return {
uid: comment.uid
};
});

View File

@ -1,69 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Valine extends Component {
render() {
const {
appId,
appKey,
notify,
verify,
placeholder,
avatar = 'mm',
avatarForce = false,
meta = ['nick', 'mail', 'link'],
pageSize = 10,
visitor = false,
highlight = true,
recordIp = false,
jsUrl
} = this.props;
if (!appId || !appKey) {
return <div class="notification is-danger">
You forgot to set the <code>app_id</code> or <code>app_key</code> for Valine.
Please set it in <code>_config.yml</code>.
</div>;
}
const js = `new Valine({
el: '#valine-thread' ,
notify: ${notify},
verify: ${verify},
appId: '${appId}',
appKey: '${appKey}',
placeholder: '${placeholder}',
avatar: '${avatar}',
avatarForce: ${avatarForce},
meta: ${JSON.stringify(meta)},
pageSize: ${pageSize},
visitor: ${visitor},
highlight: ${highlight},
recordIP: ${recordIp}
});`;
return <Fragment>
<div id="valine-thread" class="content"></div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src={jsUrl}></script>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Valine, 'comment.valine', props => {
const { comment, helper } = props;
return {
appId: comment.app_id,
appKey: comment.app_key,
notify: comment.notify,
verify: comment.verify,
placeholder: comment.placeholder,
avatar: comment.avatar,
avatarForce: comment.avatar_force,
meta: comment.meta,
pageSize: comment.page_size,
visitor: comment.visitor,
highlight: comment.highlight,
recordIp: comment.record_ip,
jsUrl: helper.cdn('valine', '1.3.10', 'dist/Valine.min.js')
};
});

View File

@ -1,28 +0,0 @@
const { Component } = require('inferno');
module.exports = class extends Component {
render() {
const { thumbnail, url, title, date, dateXml, categories } = this.props;
const categoryTags = [];
categories.forEach((category, i) => {
categoryTags.push(<a class="link-muted" href={category.url}>{category.name}</a>);
if (i < categories.length - 1) {
categoryTags.push(' / ');
}
});
return <article class="media">
{thumbnail ? <a href={url} class="media-left">
<p class="image is-64x64">
<img class="thumbnail" src={thumbnail} alt={title} />
</p>
</a> : null}
<div class="media-content size-small">
<p><time dateTime={dateXml}>{date}</time></p>
<p class="title is-6"><a href={url} class="link-muted">{title}</a></p>
<p class="is-uppercase">{categoryTags.length ? categoryTags : null}</p>
</div>
</article>;
}
};

View File

@ -1,5 +1,6 @@
const logger = require('hexo-log')();
const { Component } = require('inferno');
const view = require('hexo-component-inferno/lib/core/view');
module.exports = class extends Component {
render() {
@ -15,7 +16,7 @@ module.exports = class extends Component {
<h3 class="title is-5">{__('article.comments')}</h3>
{(() => {
try {
const Comment = require('../comment/' + comment.type);
const Comment = view.require('comment/' + comment.type);
return <Comment config={config} page={page} helper={helper} comment={comment} />;
} catch (e) {
logger.w(`Icarus cannot load comment "${comment.type}"`);

View File

@ -1,5 +1,6 @@
const logger = require('hexo-log')();
const { Component } = require('inferno');
const view = require('hexo-component-inferno/lib/core/view');
module.exports = class extends Component {
render() {
@ -17,7 +18,7 @@ module.exports = class extends Component {
const type = service.type;
if (typeof type === 'string') {
try {
const Donate = require('../donate/' + type);
const Donate = view.require('donate/' + type);
return <Donate helper={helper} donate={service} />;
} catch (e) {
logger.w(`Icarus cannot load donate button "${type}"`);

View File

@ -1,5 +1,5 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
const { cacheComponent } = require('hexo-component-inferno/lib/util/cache');
class Footer extends Component {
render() {

View File

@ -1,7 +1,7 @@
const { Component } = require('inferno');
const MetaTags = require('../misc/meta');
const OpenGraph = require('../misc/open_graph');
const StructuredData = require('../misc/structured_data');
const MetaTags = require('hexo-component-inferno/lib/view/misc/meta');
const OpenGraph = require('hexo-component-inferno/lib/view/misc/open_graph');
const StructuredData = require('hexo-component-inferno/lib/view/misc/structured_data');
const Plugins = require('./plugins');
function getPageTitle(page, siteTitle, helper) {

View File

@ -1,6 +1,6 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
const classname = require('../util/classname');
const { cacheComponent } = require('hexo-component-inferno/lib/util/cache');
const classname = require('hexo-component-inferno/lib/util/classname');
function isSameLink(a, b) {
function santize(url) {

View File

@ -1,5 +1,6 @@
const logger = require('hexo-log')();
const { Component, Fragment } = require('inferno');
const view = require('hexo-component-inferno/lib/core/view');
module.exports = class extends Component {
render() {
@ -13,7 +14,7 @@ module.exports = class extends Component {
return null;
}
try {
const Plugin = require('../plugin/' + name);
const Plugin = view.require('plugin/' + name);
return <Plugin site={site} config={config} page={page} helper={helper} plugin={plugins[name]} head={head} />;
} catch (e) {
logger.w(`Icarus cannot load plugin "${name}"`);

View File

@ -1,5 +1,6 @@
const logger = require('hexo-log')();
const { Component } = require('inferno');
const view = require('hexo-component-inferno/lib/core/view');
module.exports = class extends Component {
render() {
@ -10,7 +11,7 @@ module.exports = class extends Component {
}
try {
const Search = require('../search/' + search.type);
const Search = view.require('search/' + search.type);
return <Search config={config} helper={helper} search={search} />;
} catch (e) {
logger.w(`Icarus cannot load search "${search.type}"`);

View File

@ -1,5 +1,6 @@
const logger = require('hexo-log')();
const { Component } = require('inferno');
const view = require('hexo-component-inferno/lib/core/view');
module.exports = class extends Component {
render() {
@ -10,7 +11,7 @@ module.exports = class extends Component {
}
try {
const Share = require('../share/' + share.type);
const Share = view.require('share/' + share.type);
return <Share config={config} page={page} helper={helper} share={share} />;
} catch (e) {
logger.w(`Icarus cannot load share button "${share.type}"`);

View File

@ -1,6 +1,7 @@
const logger = require('hexo-log')();
const { Component } = require('inferno');
const classname = require('../util/classname');
const view = require('hexo-component-inferno/lib/core/view');
const classname = require('hexo-component-inferno/lib/util/classname');
function formatWidgets(widgets) {
const result = {};
@ -80,7 +81,7 @@ class Widgets extends Component {
return null;
}
try {
const Widget = require('../widget/' + widget.type);
const Widget = view.require('widget/' + widget.type);
return <Widget site={site} helper={helper} config={config} page={page} widget={widget} />;
} catch (e) {
logger.w(`Icarus cannot load widget "${widget.type}"`);

0
layout/donate/.gitkeep Normal file
View File

View File

@ -1,30 +0,0 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Alipay extends Component {
render() {
const { title, qrcode } = this.props;
if (!qrcode) {
return <div class="notification is-danger">
You forgot to set the <code>qrcode</code> for Alipay.
Please set it in <code>_config.yml</code>.
</div>;
}
return <a class="button is-info donate">
<span class="icon is-small">
<i class="fab fa-alipay"></i>
</span>
<span>{title}</span>
<span class="qrcode"><img src={qrcode} alt={title} /></span>
</a>;
}
}
module.exports = cacheComponent(Alipay, 'donate.alipay', props => {
const { donate, helper } = props;
return {
title: helper.__('donate.' + donate.type),
qrcode: helper.url_for(donate.qrcode)
};
});

View File

@ -1,33 +0,0 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
class BuyMeACoffee extends Component {
render() {
const { title, url } = this.props;
if (!url) {
return <div class="notification is-danger">
You forgot to set the <code>url</code> for &quot;Buy me a coffee&quot;.
Please set it in <code>_config.yml</code>.
</div>;
}
return <a class="button donate" href={url} style={{
'background-color': 'rgba(255,128,62,.87)',
'border-color': 'transparent',
'color': 'white'
}} target="_blank" rel="noopener">
<span class="icon is-small">
<i class="fas fa-coffee"></i>
</span>
<span>{title}</span>
</a>;
}
}
module.exports = cacheComponent(BuyMeACoffee, 'donate.buymeacoffee', props => {
const { donate, helper } = props;
return {
url: helper.url_for(donate.url),
title: helper.__('donate.' + donate.type)
};
});

View File

@ -1,29 +0,0 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Patreon extends Component {
render() {
const { title, url } = this.props;
if (!url) {
return <div class="notification is-danger">
You forgot to set the <code>url</code> for Patreon.
Please set it in <code>_config.yml</code>.
</div>;
}
return <a class="button is-danger donate" href={url} target="_blank" rel="noopener">
<span class="icon is-small">
<i class="fab fa-patreon"></i>
</span>
<span>{title}</span>
</a>;
}
}
module.exports = cacheComponent(Patreon, 'donate.petreon', props => {
const { donate, helper } = props;
return {
url: helper.url_for(donate.url),
title: helper.__('donate.' + donate.type)
};
});

View File

@ -1,37 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Paypal extends Component {
render() {
const { title, business, currencyCode } = this.props;
if (!business || !currencyCode) {
return <div class="notification is-danger">
You forgot to set the <code>business</code> or <code>currency_code</code> for Paypal.
Please set it in <code>_config.yml</code>.
</div>;
}
return <Fragment>
<a class="button is-warning donate" onclick="document.getElementById('paypal-donate-form').submit()">
<span class="icon is-small">
<i class="fab fa-paypal"></i>
</span>
<span>{title}</span>
</a>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank" rel="noopener" id="paypal-donate-form">
<input type="hidden" name="cmd" value="_donations" />
<input type="hidden" name="business" value={business} />
<input type="hidden" name="currency_code" value={currencyCode} />
</form>
</Fragment>;
}
}
module.exports = cacheComponent(Paypal, 'donate.paypal', props => {
const { donate, helper } = props;
return {
business: donate.business,
currencyCode: donate.currency_code,
title: helper.__('donate.' + donate.type)
};
});

View File

@ -1,30 +0,0 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Wechat extends Component {
render() {
const { title, qrcode } = this.props;
if (!qrcode) {
return <div class="notification is-danger">
You forgot to set the <code>qrcode</code> for Wechat.
Please set it in <code>_config.yml</code>.
</div>;
}
return <a class="button is-success donate">
<span class="icon is-small">
<i class="fab fa-weixin"></i>
</span>
<span>{title}</span>
<span class="qrcode"><img src={qrcode} alt={title} /></span>
</a>;
}
}
module.exports = cacheComponent(Wechat, 'donate.wechat', props => {
const { donate, helper } = props;
return {
qrcode: helper.url_for(donate.qrcode),
title: helper.__('donate.' + donate.type)
};
});

View File

@ -1,6 +1,6 @@
const { Component, Fragment } = require('inferno');
const Paginator = require('hexo-component-inferno/lib/view/misc/paginator');
const Article = require('./common/article');
const Paginator = require('./misc/paginator');
module.exports = class extends Component {
render() {

View File

@ -1,11 +1,11 @@
const { Component } = require('inferno');
const classname = require('hexo-component-inferno/lib/util/classname');
const Head = require('./common/head');
const Navbar = require('./common/navbar');
const Widgets = require('./common/widgets');
const Footer = require('./common/footer');
const Scripts = require('./common/scripts');
const Search = require('./common/search');
const classname = require('./util/classname');
module.exports = class extends Component {
render() {

0
layout/misc/.gitkeep Normal file
View File

View File

@ -1,41 +0,0 @@
const { Component, Fragment } = require('inferno');
function trim(str) {
return str.trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1');
}
function split(str, sep) {
const result = [];
let matched = null;
while ((matched = sep.exec(str)) !== null) {
result.push(matched[0]);
}
return result;
}
module.exports = class extends Component {
render() {
let { meta = [] } = this.props;
if (!Array.isArray(meta)) {
meta = [meta];
}
const tags = meta.filter(entry => typeof entry === 'string')
.map(entry => {
const props = split(entry, /(?:[^\\;]+|\\.)+/g)
.map(property => {
const entry = split(property, /(?:[^\\=]+|\\.)+/g);
if (entry.length < 2) {
return null;
}
return { [trim(entry[0])]: trim(entry[1]) };
}).filter(property => {
return property !== null;
}).reduce((prev, current) => {
return Object.assign(prev, current);
}, {});
return <meta {...props} />;
});
return <Fragment>{tags}</Fragment>;
}
};

View File

@ -1,145 +0,0 @@
// adapted from hexo/lib/plugins/helper/open_graph.js
const urlFn = require('url');
const moment = require('moment');
const { Component, Fragment } = require('inferno');
const { encodeURL, stripHTML, escapeHTML } = require('hexo-util');
const localeMap = {
'en': 'en_US',
'de': 'de_DE',
'es': 'es_ES',
'fr': 'fr_FR',
'hu': 'hu_HU',
'id': 'id_ID',
'it': 'it_IT',
'ja': 'ja_JP',
'ko': 'ko_KR',
'nl': 'nl_NL',
'ru': 'ru_RU',
'th': 'th_TH',
'tr': 'tr_TR',
'vi': 'vi_VN'
};
const localeRegex = new RegExp(Object.keys(localeMap).join('|'), 'i');
module.exports = class extends Component {
render() {
const {
type,
title,
date,
updated,
author,
url,
siteName,
twitterCard,
twitterSite,
googlePlus,
facebookAdmins,
facebookAppId
} = this.props;
let {
description,
language,
images,
keywords,
twitterId
} = this.props;
const htmlTags = [];
if (description) {
description = escapeHTML(stripHTML(description).substring(0, 200).trim())
.replace(/\n/g, ' ');
htmlTags.push(<meta description={description} />);
}
htmlTags.push(<meta property='og:type' content={type || 'website'} />);
htmlTags.push(<meta property='og:title' content={title} />);
htmlTags.push(<meta property='og:url' content={encodeURL(url)} />);
htmlTags.push(<meta property='og:site_name' content={siteName} />);
if (description) {
htmlTags.push(<meta property='og:description' content={description} />);
}
if (language) {
if (language.length === 2) {
language = language.replace(localeRegex, str => localeMap[str]);
htmlTags.push(<meta property='og:locale' content={language} />);
} else if (language.length === 5) {
const territory = language.slice(-2);
const territoryRegex = new RegExp(territory.concat('$'));
language = language.replace('-', '_').replace(territoryRegex, territory.toUpperCase());
htmlTags.push(<meta property='og:locale' content={language} />);
}
}
if (!Array.isArray(images)) {
images = [images];
}
images = images.map(path => {
if (!urlFn.parse(path).host) {
// resolve `path`'s absolute path relative to current page's url
// `path` can be both absolute (starts with `/`) or relative.
return urlFn.resolve(url, path);
}
htmlTags.push(<meta property='og:image' content={path} />);
return path;
});
if (date && (moment.isMoment(date) || moment.isDate(date)) && !isNaN(date.valueOf())) {
htmlTags.push(<meta property='article:published_time' content={date.toISOString()} />);
}
if (updated && (moment.isMoment(updated) || moment.isDate(updated)) && !isNaN(updated.valueOf())) {
htmlTags.push(<meta property='article:modified_time' content={updated.toISOString()} />);
}
if (author) {
htmlTags.push(<meta property='article:author' content={author} />);
}
if (keywords) {
if (typeof keywords === 'string') {
keywords = keywords.split(',');
}
keywords.map(tag => {
return tag.name ? tag.name : tag;
}).filter(Boolean).forEach(keyword => {
htmlTags.push(<meta property='article:tag' content={keyword} />);
});
}
htmlTags.push(<meta property='twitter:card' content={twitterCard || 'summary'} />);
if (images.length) {
htmlTags.push(<meta property='twitter:image' content={images[0]} />);
}
if (twitterId) {
if (twitterId[0] !== '@') {
twitterId = `@${twitterId}`;
}
htmlTags.push(<meta property='twitter:creator' content={twitterId} />);
}
if (twitterSite) {
htmlTags.push(<meta property='twitter:site' content={twitterSite} />);
}
if (googlePlus) {
htmlTags.push(<link rel="publisher" href={googlePlus} />);
}
if (facebookAdmins) {
htmlTags.push(<meta property='fb:admins' content={facebookAdmins} />);
}
if (facebookAppId) {
htmlTags.push(<meta property='fb:app_id' content={facebookAppId} />);
}
return <Fragment>{htmlTags}</Fragment>;
}
};

View File

@ -1,53 +0,0 @@
const { Component } = require('inferno');
module.exports = class extends Component {
render() {
const { current, total, baseUrl, path, urlFor, prevTitle, nextTitle } = this.props;
function getPageUrl(i) {
return urlFor(i === 1 ? baseUrl : baseUrl + path + '/' + i + '/');
}
function pagination(c, m) {
const current = c;
const last = m;
const delta = 1;
const left = current - delta;
const right = current + delta + 1;
const range = [];
const elements = [];
let l;
for (let i = 1; i <= last; i++) {
if (i === 1 || i === last || (i >= left && i < right)) {
range.push(i);
}
}
for (const i of range) {
if (l) {
if (i - l === 2) {
elements.push(<li><a class="pagination-link" href={getPageUrl(l + 1)}>{l + 1}</a></li>);
} else if (i - l !== 1) {
elements.push(<li><span class="pagination-ellipsis" dangerouslySetInnerHTML={{ __html: '&hellip;' }}></span></li>);
}
}
elements.push(<li><a class={`pagination-link${c === i ? ' is-current' : ''}`} href={getPageUrl(i)}>{i}</a></li>);
l = i;
}
return elements;
}
return <nav class="pagination is-centered mt-4" role="navigation" aria-label="pagination">
<div class={`pagination-previous${current > 1 ? '' : ' is-invisible is-hidden-mobile'}`}>
<a href={getPageUrl(current - 1)}>{prevTitle}</a>
</div>
<div class={`pagination-next${current < total ? '' : ' is-invisible is-hidden-mobile'}`}>
<a href={getPageUrl(current + 1)}>{nextTitle}</a>
</div>
<ul class="pagination-list is-hidden-mobile">
{pagination(current, total)}
</ul>
</nav>;
}
};

View File

@ -1,56 +0,0 @@
const urlFn = require('url');
const moment = require('moment');
const { Component } = require('inferno');
const { stripHTML, escapeHTML } = require('hexo-util');
module.exports = class extends Component {
render() {
const { title, url, author } = this.props;
let { description, images, date, updated } = this.props;
if (description) {
description = escapeHTML(stripHTML(description).substring(0, 200).trim())
.replace(/\n/g, ' ');
}
if (!Array.isArray(images)) {
images = [images];
}
images = images.map(path => {
if (!urlFn.parse(path).host) {
// resolve `path`'s absolute path relative to current page's url
// `path` can be both absolute (starts with `/`) or relative.
return urlFn.resolve(url, path);
}
return path;
}).filter(url => url.endsWith('.jpg') || url.endsWith('.png') || url.endsWith('.gif'));
if (date && (moment.isMoment(date) || moment.isDate(date)) && !isNaN(date.valueOf())) {
date = date.toISOString();
}
if (updated && (moment.isMoment(updated) || moment.isDate(updated)) && !isNaN(updated.valueOf())) {
updated = updated.toISOString();
}
const data = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
'mainEntityOfPage': {
'@type': 'WebPage',
'@id': url
},
'headline': title,
'image': images,
'datePublished': date,
'dateModified': updated,
'author': {
'@type': 'Person',
'name': author
},
'description': description
};
return <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}></script>;
}
};

View File

@ -1,5 +1,5 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
const { cacheComponent } = require('hexo-component-inferno/lib/util/cache');
class AnimeJs extends Component {
render() {

View File

@ -1,5 +1,5 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
const { cacheComponent } = require('hexo-component-inferno/lib/util/cache');
class BackToTop extends Component {
render() {

View File

@ -1,28 +0,0 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
class BaiduAnalytics extends Component {
render() {
const { trackingId } = this.props;
const js = `var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?${trackingId}";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();`;
return <script dangerouslySetInnerHTML={{ __html: js }}></script>;
}
}
module.exports = cacheComponent(BaiduAnalytics, 'plugin.baiduanalytics', props => {
const { head, plugin } = props;
if (!head || !plugin.tracking_id) {
return null;
}
return {
trackingId: plugin.tracking_id
};
});

View File

@ -1,15 +0,0 @@
const { Component } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Busuanzi extends Component {
render() {
return <script src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" defer={true}></script>;
}
}
module.exports = cacheComponent(Busuanzi, 'plugin.busuanzi', props => {
if (!props.head) {
return null;
}
return {};
});

View File

@ -1,36 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Gallery extends Component {
render() {
const { head, jsUrl, lightGallery, justifiedGallery } = this.props;
if (head) {
return <Fragment>
<link rel="stylesheet" href={lightGallery.cssUrl} />
<link rel="stylesheet" href={justifiedGallery.cssUrl} />
</Fragment>;
}
return <Fragment>
<script src={lightGallery.jsUrl} defer={true}></script>
<script src={justifiedGallery.jsUrl} defer={true}></script>
<script src={jsUrl} defer={true}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Gallery, 'plugin.gallery', props => {
const { head, helper } = props;
return {
head,
jsUrl: helper.url_for('/js/gallery.js'),
lightGallery: {
jsUrl: helper.cdn('lightgallery', '1.6.8', 'dist/js/lightgallery.min.js'),
cssUrl: helper.cdn('lightgallery', '1.6.8', 'dist/css/lightgallery.min.css')
},
justifiedGallery: {
jsUrl: helper.cdn('justifiedGallery', '3.7.0', 'dist/js/jquery.justifiedGallery.min.js'),
cssUrl: helper.cdn('justifiedGallery', '3.7.0', 'dist/css/justifiedGallery.min.css')
}
};
});

View File

@ -1,29 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class GoogleAnalytics extends Component {
render() {
const { trackingId } = this.props;
const js = `window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${trackingId}');`;
return <Fragment>
<script src={`https://www.googletagmanager.com/gtag/js?id=${trackingId}`} async={true}></script>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(GoogleAnalytics, 'plugin.googleanalytics', props => {
const { head, plugin } = props;
if (!head || !plugin.tracking_id) {
return null;
}
return {
trackingId: plugin.tracking_id
};
});

View File

@ -1,31 +0,0 @@
const { Component, Fragment } = require('inferno');
const { cacheComponent } = require('../util/cache');
class Hotjar extends Component {
render() {
const { siteId } = this.props;
const js = `(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:${siteId},hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');`;
return <Fragment>
<script dangerouslySetInnerHTML={{ __html: js }}></script>
</Fragment>;
}
}
module.exports = cacheComponent(Hotjar, 'plugin.hotjar', props => {
const { head, plugin } = props;
if (!head || !plugin.site_id) {
return null;
}
return {
siteId: plugin.site_id
};
});

Some files were not shown because too many files have changed in this diff Show More