refactor(layout): common layout comps to jsx
parent
7d544c5c3b
commit
467636b50a
|
@ -28,7 +28,9 @@
|
|||
"inferno",
|
||||
"inferno-create-element",
|
||||
"hexo-util",
|
||||
"cheerio"
|
||||
"hexo-log",
|
||||
"cheerio",
|
||||
"moment"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="level">
|
||||
<div class="level-start has-text-centered-mobile">
|
||||
<a class="footer-logo is-block has-mb-6" href="<%- url_for('/') %>">
|
||||
<% if (logo && logo.text) { %>
|
||||
<%= logo.text %>
|
||||
<% } else { %>
|
||||
<img src="<%- url_for(logo) %>" alt="<%= title %>" height="28">
|
||||
<% } %>
|
||||
</a>
|
||||
<p class="is-size-7">
|
||||
© <%= date(new Date(), 'YYYY') %> <%= author || title %>
|
||||
Powered by <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a> & <a
|
||||
href="https://github.com/ppoffice/hexo-theme-icarus" target="_blank" rel="noopener">Icarus</a>
|
||||
<% if (busuanzi) { %>
|
||||
<br>
|
||||
<span id="busuanzi_container_site_uv">
|
||||
<%- _p('plugin.visitor', '<span id="busuanzi_value_site_uv">0</span>') %>
|
||||
</span>
|
||||
<% } %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="level-end">
|
||||
<% if (Object.keys(links).length) { %>
|
||||
<div class="field has-addons is-flex-center-mobile has-mt-5-mobile is-flex-wrap is-flex-middle">
|
||||
<% for (let name in links) {
|
||||
let link = links[name]; %>
|
||||
<p class="control">
|
||||
<a class="button is-white <%= typeof(link) !== 'string' ? 'is-large' : '' %>" target="_blank" rel="noopener" title="<%= name %>" href="<%= url_for(typeof(link) === 'string' ? link : link.url) %>">
|
||||
<% if (typeof(link) === 'string') { %>
|
||||
<%= name %>
|
||||
<% } else { %>
|
||||
<i class="<%= link.icon %>"></i>
|
||||
<% } %>
|
||||
</a>
|
||||
</p>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
const { Component } = require('inferno');
|
||||
const { cacheComponent } = require('../util/cache');
|
||||
|
||||
class Footer extends Component {
|
||||
render() {
|
||||
const {
|
||||
logo,
|
||||
logoUrl,
|
||||
siteUrl,
|
||||
siteTitle,
|
||||
siteYear,
|
||||
author,
|
||||
links,
|
||||
showVisitorCounter,
|
||||
visitorCounterTitle
|
||||
} = this.props;
|
||||
|
||||
return <footer className="footer">
|
||||
<div className="container">
|
||||
<div className="level">
|
||||
<div className="level-start has-text-centered-mobile">
|
||||
<a className="footer-logo is-block has-mb-6" href={siteUrl}>
|
||||
{logo && logo.text ? logo.text : <img src={logoUrl} alt={siteTitle} height="28" />}
|
||||
</a>
|
||||
<p className="is-size-7">
|
||||
<span dangerouslySetInnerHTML={{ __html: `© ${siteYear} ${author || siteTitle}` }}></span>
|
||||
Powered by <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a> &
|
||||
<a href="https://github.com/ppoffice/hexo-theme-icarus" target="_blank" rel="noopener">Icarus</a>
|
||||
{showVisitorCounter ? <br /> : null}
|
||||
{showVisitorCounter ? <span id="busuanzi_container_site_uv"
|
||||
dangerouslySetInnerHTML={{ __html: visitorCounterTitle }}></span> : null}
|
||||
</p>
|
||||
</div>
|
||||
<div className="level-end">
|
||||
{Object.keys(links).length ? <div className="field has-addons is-flex-center-mobile has-mt-5-mobile is-flex-wrap is-flex-middle">
|
||||
{Object.keys(links).map(name => {
|
||||
const link = links[name];
|
||||
return <p className="control">
|
||||
<a className={`button is-white ${link.icon ? 'is-large' : ''}`} target="_blank" rel="noopener" title={name} href={link.url}>
|
||||
{link.icon ? name : <i className={link.icon}></i>}
|
||||
</a>
|
||||
</p>;
|
||||
})}
|
||||
</div> : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = cacheComponent(Footer, 'common.footer', props => {
|
||||
const { config, helper } = this.props;
|
||||
const { url_for, _p, date } = helper;
|
||||
const { logo, title, author, footer, plugins } = config;
|
||||
|
||||
const links = {};
|
||||
if (footer && footer.links) {
|
||||
Object.keys(footer.links).forEach(name => {
|
||||
const link = footer.links[name];
|
||||
links[name] = {
|
||||
url: url_for(typeof link === 'string' ? link : link.url),
|
||||
icon: link.icon
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
logo,
|
||||
logoUrl: url_for(logo),
|
||||
siteUrl: url_for('/'),
|
||||
siteTitle: title,
|
||||
siteYear: date(new Date(), 'YYYY'),
|
||||
author,
|
||||
links,
|
||||
showVisitorCounter: plugins && plugins.busuanzi === true,
|
||||
visitorCounterTitle: _p('plugin.visitor', '<span id="busuanzi_value_site_uv">0</span>')
|
||||
};
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
module.exports = (ctx, locals) => {
|
||||
const { get_config } = ctx;
|
||||
return Object.assign(locals, {
|
||||
title: get_config('title'),
|
||||
author: get_config('author'),
|
||||
logo: get_config('logo'),
|
||||
busuanzi: get_config('plugins.busuanzi', false),
|
||||
links: get_config('footer.links', [])
|
||||
});
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
<meta charset="utf-8" />
|
||||
<% if (get_config('meta_generator', true)) { %>
|
||||
<meta name="generator" content="Hexo <%= hexo_version() %>" />
|
||||
<% } %>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<%- meta() %>
|
||||
<title><%= page_title() %></title>
|
||||
|
||||
<% if (has_config('open_graph')) { %>
|
||||
<%- open_graph({
|
||||
twitter_id: get_config('open_graph.twitter_id'),
|
||||
twitter_site: get_config('open_graph.twitter_site'),
|
||||
google_plus: get_config('open_graph.google_plus'),
|
||||
fb_admins: get_config('open_graph.fb_admins'),
|
||||
fb_app_id: get_config('open_graph.fb_app_id'),
|
||||
image: get_og_image(page)
|
||||
}) %>
|
||||
<% } %>
|
||||
|
||||
<% if (has_config('canonical_url')) { %>
|
||||
<link rel="canonical" href="<%- get_config('canonical_url') %>" />
|
||||
<% } %>
|
||||
|
||||
<% if (has_config('rss')) { %>
|
||||
<link rel="alternative" href="<%- get_config('rss') %>" title="<%= get_config('title') %>" type="application/atom+xml">
|
||||
<% } %>
|
||||
|
||||
<% if (has_config('favicon')) { %>
|
||||
<link rel="icon" href="<%- url_for(get_config('favicon')) %>">
|
||||
<% } %>
|
||||
|
||||
<%- _css(cdn('bulma', '0.7.2', 'css/bulma.css')) %>
|
||||
<%- _css(iconcdn()) %>
|
||||
<%- _css(fontcdn('Ubuntu:400,600|Source+Code+Pro')) %>
|
||||
<%- _css(cdn('highlight.js', '9.12.0', 'styles/' + get_config('article.highlight.theme') + '.css')) %>
|
||||
|
||||
<% if (has_config('plugins')) { %>
|
||||
<% for (let plugin in get_config('plugins')) { %>
|
||||
<%- _partial('plugin/' + plugin, { head: true, plugin: get_config('plugins')[plugin] }) %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
<%- _css('css/style') %>
|
|
@ -0,0 +1,76 @@
|
|||
'use strict';
|
||||
|
||||
const { Component } = require('inferno');
|
||||
const MetaTags = require('../misc/meta');
|
||||
const OpenGraph = require('../misc/open_graph');
|
||||
const Plugins = require('./plugins');
|
||||
|
||||
module.exports = class extends Component {
|
||||
render() {
|
||||
const { env, site, config, helper, page } = this.props;
|
||||
const { is_post, url_for, cdn, iconcdn, fontcdn } = helper;
|
||||
const {
|
||||
url,
|
||||
meta_generator = true,
|
||||
meta = [],
|
||||
open_graph,
|
||||
canonical_url,
|
||||
rss,
|
||||
favicon,
|
||||
article,
|
||||
highlight
|
||||
} = config;
|
||||
|
||||
let hlTheme;
|
||||
if (highlight && highlight.enable === false) {
|
||||
hlTheme = null;
|
||||
} else if (article && article.highlight && article.hightlight.theme) {
|
||||
hlTheme = article.hightlight.theme;
|
||||
} else {
|
||||
hlTheme = 'atom-one-light';
|
||||
}
|
||||
|
||||
const images = [];
|
||||
if (page.content && page.content.includes('<img')) {
|
||||
let img;
|
||||
const imgPattern = /<img [^>]*src=['"]([^'"]+)([^>]*>)/gi;
|
||||
while ((img = imgPattern.exec(page.content)) !== null) {
|
||||
images.push(img[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return <head>
|
||||
<meta charset="utf-8" />
|
||||
{meta_generator ? <meta name="generator" content={`Hexo ${env.version}`} /> : null}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<MetaTags meta={meta} />
|
||||
|
||||
{open_graph ? <OpenGraph
|
||||
type={is_post() ? 'article' : 'website'}
|
||||
title={page.title || config.title}
|
||||
date={page.date}
|
||||
updated={page.updated}
|
||||
author={config.author}
|
||||
description={page.description || page.excerpt || page.content || config.description}
|
||||
keywords={page.keywords || (page.tags && page.tags.length ? page.tags : undefined) || config.keywords}
|
||||
url={url}
|
||||
images={page.photos || images}
|
||||
siteName={config.title}
|
||||
language={page.lang || page.language || config.language}
|
||||
twitterId={open_graph.twitter}
|
||||
googlePlus={open_graph.google_plus}
|
||||
facebookAdmins={open_graph.fb_admins}
|
||||
facebookAppId={open_graph.fb_app_id} /> : null}
|
||||
|
||||
{canonical_url ? <link rel="canonical" href={canonical_url} /> : null}
|
||||
{rss ? <link rel="alternative" href={url_for(rss)} title={config.title} type="application/atom+xml" /> : null}
|
||||
{favicon ? <link rel="icon" href={url_for(favicon)} /> : null}
|
||||
<link rel="stylesheet" href={cdn('bulma', '0.7.2', 'css/bulma.css')} />
|
||||
<link rel="stylesheet" href={iconcdn()} />
|
||||
<link rel="stylesheet" href={fontcdn('Ubuntu:400,600|Source+Code+Pro')} />
|
||||
{hlTheme ? <link rel="stylesheet" href={cdn('highlight.js', '9.12.0', 'styles/' + hlTheme + '.css')} /> : null}
|
||||
<Plugins site={site} config={config} helper={helper} page={page} head={true} />
|
||||
<link rel="stylesheet" href={url_for('/css/style')} />
|
||||
</head>;
|
||||
}
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
<nav class="navbar navbar-main">
|
||||
<div class="container">
|
||||
<div class="navbar-brand is-flex-center">
|
||||
<a class="navbar-item navbar-logo" href="<%- url_for('/') %>">
|
||||
<% if (logo && logo.text) { %>
|
||||
<%= logo.text %>
|
||||
<% } else { %>
|
||||
<img src="<%- url_for(logo) %>" alt="<%= title %>" height="28">
|
||||
<% } %>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<% if (Object.keys(menus).length) { %>
|
||||
<div class="navbar-start">
|
||||
<% for (let i in menus) { let menu = menus[i]; %>
|
||||
<a class="navbar-item<% if (actives[i]) { %> is-active<% } %>"
|
||||
href="<%- url_for(menu) %>"><%= i %></a>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="navbar-end">
|
||||
<% if (Object.keys(links).length) { %>
|
||||
<% for (let name in links) {
|
||||
let link = links[name]; %>
|
||||
<a class="navbar-item" target="_blank" rel="noopener" title="<%= name %>" href="<%= url_for(typeof(link) === 'string' ? link : link.url) %>">
|
||||
<% if (typeof(link) === 'string') { %>
|
||||
<%= name %>
|
||||
<% } else { %>
|
||||
<i class="<%= link.icon %>"></i>
|
||||
<% } %>
|
||||
</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% if (hasToc) { %>
|
||||
<a class="navbar-item is-hidden-tablet catalogue" title="<%= _p('widget.catalogue', Infinity) %>" href="javascript:;">
|
||||
<i class="fas fa-list-ul"></i>
|
||||
</a>
|
||||
<% } %>
|
||||
<% if (search) { %>
|
||||
<a class="navbar-item search" title="<%= __('search.search') %>" href="javascript:;">
|
||||
<i class="fas fa-search"></i>
|
||||
</a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,109 @@
|
|||
'use strict';
|
||||
|
||||
const { Component, Fragment } = require('inferno');
|
||||
const { cacheComponent } = require('../util/cache');
|
||||
|
||||
function isSameLink(a, b) {
|
||||
function santize(url) {
|
||||
let paths = url.replace(/(^\w+:|^)\/\//, '').split('#')[0].split('/').filter(p => p.trim() !== '');
|
||||
if (paths.length > 0 && paths[paths.length - 1].trim() === 'index.html') {
|
||||
paths = paths.slice(0, paths.length - 1);
|
||||
}
|
||||
return paths.join('/');
|
||||
}
|
||||
return santize(a) === santize(b);
|
||||
}
|
||||
|
||||
class Navbar extends Component {
|
||||
render() {
|
||||
const {
|
||||
logo,
|
||||
logoUrl,
|
||||
siteUrl,
|
||||
siteTitle,
|
||||
menus,
|
||||
links,
|
||||
showToc,
|
||||
tocTitle,
|
||||
showSearch,
|
||||
searchTitle
|
||||
} = this.props;
|
||||
|
||||
return <nav className="navbar navbar-main">
|
||||
<div className="container">
|
||||
<div className="navbar-brand is-flex-center">
|
||||
<a className="navbar-item navbar-logo" href={siteUrl}>
|
||||
{logo && logo.text ? logo.text : <img src={logoUrl} alt={siteTitle} height="28" />}
|
||||
</a>
|
||||
</div>
|
||||
<div className="navbar-menu">
|
||||
{Object.keys(menus).length ? <div className="navbar-start">
|
||||
{Object.keys(menus).map(name => {
|
||||
const menu = menus[name];
|
||||
return <a className={{ 'navbar-item': true, 'is-active': menu.active }} href={menu.url}>{name}</a>;
|
||||
})}
|
||||
</div> : null}
|
||||
<div className="navbar-end">
|
||||
{Object.keys(links).length ? <Fragment>
|
||||
{Object.keys(links).forEach(name => {
|
||||
const link = links[name];
|
||||
return <a className="navbar-item" target="_blank" rel="noopener" title={name} href={link.url}>
|
||||
{link.icon ? <i className={link.icon}></i> : name}
|
||||
</a>;
|
||||
})}
|
||||
</Fragment> : null}
|
||||
{showToc ? <a className="navbar-item is-hidden-tablet catalogue" title={tocTitle} href="javascript:;">
|
||||
<i className="fas fa-list-ul"></i>
|
||||
</a> : null}
|
||||
{showSearch ? <a className="navbar-item search" title={searchTitle} href="javascript:;">
|
||||
<i className="fas fa-search"></i>
|
||||
</a> : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = cacheComponent(Navbar, 'common.navbar', props => {
|
||||
const { config, helper, page } = this.props;
|
||||
const { url_for, _p, __ } = helper;
|
||||
const { logo, title, navbar, widgets, search } = config;
|
||||
|
||||
const hasTocWidget = Array.isArray(widgets) && widgets.find(widget => widget.type === 'toc');
|
||||
const showToc = (config.toc === true || page.toc) && hasTocWidget && ['page', 'post'].includes(page.layout);
|
||||
|
||||
const menus = {};
|
||||
if (navbar && navbar.menus) {
|
||||
const pageUrl = typeof page.path !== 'undefined' ? url_for(page.path) : '';
|
||||
Object.keys(navbar.menus).forEach(name => {
|
||||
const url = url_for(navbar.menus[name]);
|
||||
const active = isSameLink(url, pageUrl);
|
||||
menus[name] = { url, active };
|
||||
});
|
||||
}
|
||||
|
||||
const links = {};
|
||||
if (navbar && navbar.links) {
|
||||
Object.keys(navbar.links).forEach(name => {
|
||||
const link = navbar.links[name];
|
||||
links[name] = {
|
||||
url: url_for(typeof link === 'string' ? link : link.url),
|
||||
icon: link.icon
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
logo,
|
||||
logoUrl: url_for(logo),
|
||||
siteUrl: url_for('/'),
|
||||
siteTitle: title,
|
||||
menus,
|
||||
links,
|
||||
showToc,
|
||||
tocTitle: _p('widget.catalogue', Infinity),
|
||||
showSearch: search && search.type,
|
||||
searchTitle: __('search.search')
|
||||
};
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
module.exports = (ctx, locals) => {
|
||||
const { get_config, has_config, has_widget, is_same_link, page } = ctx;
|
||||
const menus = get_config('navbar.menu', {});
|
||||
const actives = {};
|
||||
Object.keys(menus).forEach(i => {
|
||||
actives[i] = typeof page.path !== 'undefined' && is_same_link(menus[i], page.path)
|
||||
});
|
||||
const hasToc = get_config('toc') === true && has_widget('toc') && (page.layout === 'page' || page.layout === 'post');
|
||||
return Object.assign(locals, {
|
||||
hasToc,
|
||||
menus,
|
||||
actives,
|
||||
title: get_config('title'),
|
||||
logo: get_config('logo'),
|
||||
links: get_config('navbar.links', []),
|
||||
search: has_config('search.type')
|
||||
});
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<% function link_url(i) {
|
||||
return url_for(i === 1 ? page.base : page.base + get_config('pagination_dir') + '/' + i + '/');
|
||||
}
|
||||
|
||||
function pagination(c, m) {
|
||||
var current = c,
|
||||
last = m,
|
||||
delta = 1,
|
||||
left = current - delta,
|
||||
right = current + delta + 1,
|
||||
range = [],
|
||||
elements = [],
|
||||
l;
|
||||
|
||||
for (let i = 1; i <= last; i++) {
|
||||
if (i == 1 || i == last || (i >= left && i < right)) {
|
||||
range.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i of range) {
|
||||
if (l) {
|
||||
if (i - l === 2) {
|
||||
elements.push(`<li><a class="pagination-link has-text-black-ter" href="${ link_url(l + 1) }">${ l + 1 }</a></li>`);
|
||||
} else if (i - l !== 1) {
|
||||
elements.push(`<li><span class="pagination-ellipsis has-text-black-ter">…</span></li>`);
|
||||
}
|
||||
}
|
||||
elements.push(`<li><a class="pagination-link${ c === i ? ' is-current' : ' has-text-black-ter'}" href="${ link_url(i) }">${ i }</a></li>`);
|
||||
l = i;
|
||||
}
|
||||
return elements;
|
||||
} %>
|
||||
<div class="card card-transparent">
|
||||
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
|
||||
<div class="pagination-previous<%= page.current > 1 ? '' : ' is-invisible is-hidden-mobile' %>">
|
||||
<a class="is-flex-grow has-text-black-ter" href="<%= link_url(page.current - 1) %>"><%= __('common.prev') %></a>
|
||||
</div>
|
||||
<div class="pagination-next<%= page.current < page.total ? '' : ' is-invisible is-hidden-mobile' %>">
|
||||
<a class="is-flex-grow has-text-black-ter" href="<%= link_url(page.current + 1) %>"><%= __('common.next') %></a>
|
||||
</div>
|
||||
<ul class="pagination-list is-hidden-mobile">
|
||||
<% pagination(page.current, page.total).forEach(element => { %>
|
||||
<%- element %>
|
||||
<% }) %>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
const logger = require('hexo-log');
|
||||
const { Component, Fragment } = require('inferno');
|
||||
|
||||
module.exports = class extends Component {
|
||||
render() {
|
||||
const { site, config, page, helper, head } = this.props;
|
||||
const { plugins = [] } = config;
|
||||
|
||||
return <Fragment>
|
||||
{Object.keys(plugins).map(name => {
|
||||
// plugin is not enabled
|
||||
if (!plugins[name]) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const Plugin = require('../plugin/' + name);
|
||||
return <Plugin site={site} config={config} page={page} helper={helper} plugin={plugins[name]} head={head} />;
|
||||
} catch (e) {
|
||||
logger.warn(`Icarus cannot load plugin "${name}"`);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</Fragment>;
|
||||
}
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
<%- _js(cdn('jquery', '3.3.1', 'dist/jquery.min.js')) %>
|
||||
<%- _js(cdn('moment', '2.22.2', 'min/moment-with-locales.min.js')) %>
|
||||
<script>moment.locale("<%= get_config('language', 'en') %>");</script>
|
||||
|
||||
<%
|
||||
let externalLink = get_config('external_link');
|
||||
if (typeof externalLink === 'boolean') {
|
||||
externalLink = { enable: externalLink, exclude: [] };
|
||||
} else {
|
||||
externalLink = {
|
||||
enable: typeof externalLink.enable === 'boolean' ? externalLink.enable : true,
|
||||
exclude: externalLink.exclude || []
|
||||
};
|
||||
}
|
||||
%>
|
||||
<script>
|
||||
var IcarusThemeSettings = {
|
||||
site: {
|
||||
url: '<%= config.url %>',
|
||||
external_link: <%- JSON.stringify(externalLink) %>
|
||||
},
|
||||
article: {
|
||||
highlight: {
|
||||
clipboard: <%= get_config('article.highlight.clipboard', true) %>,
|
||||
fold: '<%= get_config('article.highlight.fold', true) %>'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<% if (get_config('article.highlight.clipboard')) { %>
|
||||
<%- _js(cdn('clipboard', '2.0.4', 'dist/clipboard.min.js'), true) %>
|
||||
<% } %>
|
||||
|
||||
<% if (has_config('plugins')) { %>
|
||||
<% for (let plugin in get_config('plugins')) { %>
|
||||
<%- _partial('plugin/' + plugin, { head: false, plugin: get_config('plugins')[plugin] }) %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
<%- _js('js/main', true) %>
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
const { Component, Fragment } = require('inferno');
|
||||
const Plugins = require('./plugins');
|
||||
|
||||
module.exports = class extends Component {
|
||||
render() {
|
||||
const { site, config, helper, page } = this.props;
|
||||
const { url_for, cdn } = helper;
|
||||
const { external_link, article } = config;
|
||||
const language = page.lang || page.language || config.language || 'en';
|
||||
|
||||
let externalLink;
|
||||
if (typeof external_link === 'boolean') {
|
||||
externalLink = { enable: external_link, exclude: [] };
|
||||
} else {
|
||||
externalLink = {
|
||||
enable: typeof external_link.enable === 'boolean' ? external_link.enable : true,
|
||||
exclude: external_link.exclude || []
|
||||
};
|
||||
}
|
||||
|
||||
let fold = 'unfolded';
|
||||
let clipboard = true;
|
||||
if (article && article.highlight) {
|
||||
if (typeof article.highlight.clipboard !== 'undefined') {
|
||||
clipboard = !!article.highlight.clipboard;
|
||||
}
|
||||
if (typeof article.highlight.fold === 'string') {
|
||||
fold = article.highlight.fold;
|
||||
}
|
||||
}
|
||||
|
||||
const embeddedConfig = `var IcarusThemeSettings = {
|
||||
site: {
|
||||
url: '${config.url}',
|
||||
external_link: ${JSON.stringify(externalLink)}
|
||||
},
|
||||
article: {
|
||||
highlight: {
|
||||
clipboard: ${clipboard},
|
||||
fold: '${fold}'
|
||||
}
|
||||
}
|
||||
};`;
|
||||
|
||||
return <Fragment>
|
||||
<script src={cdn('jquery', '3.3.1', 'dist/jquery.min.js')}></script>
|
||||
<script src={cdn('moment', '2.22.2', 'min/moment-with-locales.min.js')}></script>
|
||||
<script dangerouslySetInnerHTML={{ __html: `moment.locale("${language}");` }}></script>
|
||||
<script dangerouslySetInnerHTML={{ __html: embeddedConfig }}></script>
|
||||
{clipboard ? <script src={cdn('clipboard', '2.0.4', 'dist/clipboard.min.js')} defer={true}></script> : null}
|
||||
<Plugins site={site} config={config} page={page} helper={helper} head={false} />
|
||||
<script src={url_for('/js/main.js')} defer={true}></script>
|
||||
</Fragment>;
|
||||
}
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
<% if (get_widgets(position).length) { %>
|
||||
<% function side_column_class() {
|
||||
switch (column_count()) {
|
||||
case 2:
|
||||
return 'is-4-tablet is-4-desktop is-4-widescreen';
|
||||
case 3:
|
||||
return 'is-4-tablet is-4-desktop is-3-widescreen';
|
||||
}
|
||||
return '';
|
||||
} %>
|
||||
<% function visibility_class() {
|
||||
if (column_count() === 3 && position === 'right') {
|
||||
return 'is-hidden-touch is-hidden-desktop-only';
|
||||
}
|
||||
return '';
|
||||
} %>
|
||||
<% function order_class() {
|
||||
return position === 'left' ? 'has-order-1' : 'has-order-3';
|
||||
} %>
|
||||
<% function sticky_class(position) {
|
||||
return get_config('sidebar.' + position + '.sticky', false) ? 'is-sticky' : '';
|
||||
} %>
|
||||
<div class="column <%= side_column_class() %> <%= visibility_class() %> <%= order_class() %> column-<%= position %> <%= sticky_class(position) %>">
|
||||
<% get_widgets(position).forEach(widget => {%>
|
||||
<%- _partial('widget/' + widget.type, { widget }) %>
|
||||
<% }) %>
|
||||
<% if (position === 'left') { %>
|
||||
<div class="column-right-shadow is-hidden-widescreen <%= sticky_class('right') %>">
|
||||
<% get_widgets('right').forEach(widget => {%>
|
||||
<%- _partial('widget/' + widget.type, { widget }) %>
|
||||
<% }) %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
|
@ -0,0 +1,102 @@
|
|||
'use strict';
|
||||
|
||||
const logger = require('hexo-log');
|
||||
const { Component } = require('inferno');
|
||||
|
||||
function formatWidgets(widgets) {
|
||||
const result = {};
|
||||
if (Array.isArray(widgets)) {
|
||||
widgets.forEach(widget => {
|
||||
if (Object.prototype.hasOwnProperty.call(widget, 'position')
|
||||
&& (widget.position === 'left' || widget.position === 'right')) {
|
||||
if (!Object.prototype.hasOwnProperty.call(result, widget.position)) {
|
||||
result[widget.position] = [widget];
|
||||
} else {
|
||||
result[widget.position].push(widget);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getColumnCount(widgets) {
|
||||
let count = 1;
|
||||
const w = formatWidgets(widgets);
|
||||
if (Object.prototype.hasOwnProperty.call(w, 'left') && w.left.length) {
|
||||
count++;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(w, 'right') && w.left.length) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function getColumnSizeClass(columnCount) {
|
||||
switch (columnCount) {
|
||||
case 2:
|
||||
return 'is-4-tablet is-4-desktop is-4-widescreen';
|
||||
case 3:
|
||||
return 'is-4-tablet is-4-desktop is-3-widescreen';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getColumnVisibilityClass(columnCount, position) {
|
||||
if (columnCount === 3 && position === 'right') {
|
||||
return 'is-hidden-touch is-hidden-desktop-only';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getColumnOrderClass(position) {
|
||||
return position === 'left' ? 'has-order-1' : 'has-order-3';
|
||||
}
|
||||
|
||||
function isColumnSticky(config, position) {
|
||||
return config.sidebar && config.sidebar[position] && config.sidebar[position].sticky === true;
|
||||
}
|
||||
|
||||
class Widgets extends Component {
|
||||
render() {
|
||||
const { site, config, helper, page, position } = this.props;
|
||||
const widgets = formatWidgets(config.widgets)[position] || [];
|
||||
const columnCount = getColumnCount(config.widgets);
|
||||
|
||||
if (!widgets.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <div className={{
|
||||
'column': true,
|
||||
['column-' + position]: true,
|
||||
[getColumnSizeClass(columnCount)]: true,
|
||||
[getColumnVisibilityClass(columnCount, position)]: true,
|
||||
[getColumnOrderClass(position)]: true,
|
||||
'is-sticky': isColumnSticky(config, position)
|
||||
}}>
|
||||
{widgets[position].map(widget => {
|
||||
// widget type is not defined
|
||||
if (!widget.type) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const Widget = require('../widget/' + widget.type);
|
||||
return <Widget site={site} helper={helper} config={config} page={page} widget={widget} />;
|
||||
} catch (e) {
|
||||
logger.warn(`Icarus cannot load widget "${widget.type}"`);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{position === 'left' ? <div className={{
|
||||
'column-right-shadow': true,
|
||||
'is-hidden-widescreen': true,
|
||||
'is-sticky': isColumnSticky(config, position)
|
||||
}}></div> : null}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
Widgets.getColumnCount = getColumnCount;
|
||||
|
||||
module.exports = Widgets;
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
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>;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,147 @@
|
|||
'use strict';
|
||||
|
||||
// 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>;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
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 className="pagination-link has-text-black-ter" href={getPageUrl(l + 1)}>{l + 1}</a></li>);
|
||||
} else if (i - l !== 1) {
|
||||
elements.push(<li><span className="pagination-ellipsis has-text-black-ter" dangerouslySetInnerHTML={{ __html: '…' }}></span></li>);
|
||||
}
|
||||
}
|
||||
elements.push(<li><a className={`pagination-link${c === i ? ' is-current' : ' has-text-black-ter'}`} href={getPageUrl(i)}>{i}</a></li>);
|
||||
l = i;
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
return <div className="card card-transparent">
|
||||
<nav className="pagination is-centered" role="navigation" aria-label="pagination">
|
||||
<div className={`pagination-previous${current > 1 ? '' : ' is-invisible is-hidden-mobile'}`}>
|
||||
<a className="is-flex-grow has-text-black-ter" href={getPageUrl(current - 1)}>{prevTitle}</a>
|
||||
</div>
|
||||
<div className={`pagination-next${current < total ? '' : ' is-invisible is-hidden-mobile'}`}>
|
||||
<a className="is-flex-grow has-text-black-ter" href={getPageUrl(current + 1)}>{nextTitle}</a>
|
||||
</div>
|
||||
<ul className="pagination-list is-hidden-mobile">
|
||||
{pagination(current, total)}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>;
|
||||
}
|
||||
};
|
|
@ -14,8 +14,9 @@ class AnimeJs extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(AnimeJs, 'plugin.animejs', props => {
|
||||
const { helper, head } = props;
|
||||
return {
|
||||
head: props.head,
|
||||
url_for: props.url_for
|
||||
head,
|
||||
url_for: helper.url_for
|
||||
};
|
||||
});
|
||||
|
|
|
@ -21,9 +21,10 @@ class BackToTop extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(BackToTop, 'plugin.backtotop', props => {
|
||||
const { helper, head } = props;
|
||||
return {
|
||||
head: props.head,
|
||||
title: props.__('plugin.backtotop'),
|
||||
url_for: props.url_for
|
||||
head,
|
||||
title: helper.__('plugin.backtotop'),
|
||||
url_for: helper.url_for
|
||||
};
|
||||
});
|
||||
|
|
|
@ -20,10 +20,11 @@ class BaiduAnalytics extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(BaiduAnalytics, 'plugin.baiduanalytics', props => {
|
||||
if (!props.head || !props.tracking_id) {
|
||||
const { head, plugin } = props;
|
||||
if (!head || !plugin.tracking_id) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
trackingId: props.tracking_id
|
||||
trackingId: plugin.tracking_id
|
||||
};
|
||||
});
|
||||
|
|
|
@ -22,16 +22,17 @@ class Gallery extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(Gallery, 'plugin.gallery', props => {
|
||||
const { head, helper } = props;
|
||||
return {
|
||||
head: props.head,
|
||||
url_for: props.url_for,
|
||||
head,
|
||||
url_for: helper.url_for,
|
||||
lightGallery: {
|
||||
jsUrl: props.cdn('lightgallery', '1.6.8', 'dist/js/lightgallery.min.js'),
|
||||
cssUrl: props.cdn('lightgallery', '1.6.8', 'dist/css/lightgallery.min.css')
|
||||
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: props.cdn('justifiedGallery', '3.7.0', 'dist/js/jquery.justifiedGallery.min.js'),
|
||||
cssUrl: props.cdn('justifiedGallery', '3.7.0', 'dist/css/justifiedGallery.min.css')
|
||||
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')
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -21,10 +21,11 @@ class GoogleAnalytics extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(GoogleAnalytics, 'plugin.googleanalytics', props => {
|
||||
if (!props.head || !props.tracking_id) {
|
||||
const { head, plugin } = props;
|
||||
if (!head || !plugin.tracking_id) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
trackingId: props.tracking_id
|
||||
trackingId: plugin.tracking_id
|
||||
};
|
||||
});
|
||||
|
|
|
@ -23,10 +23,11 @@ class Hotjar extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(Hotjar, 'plugin.hotjar', props => {
|
||||
if (!props.head || !props.site_id) {
|
||||
const { head, plugin } = props;
|
||||
if (!head || !plugin.site_id) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
siteId: props.site_id
|
||||
siteId: plugin.site_id
|
||||
};
|
||||
});
|
||||
|
|
|
@ -35,10 +35,11 @@ class Mathjax extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(Mathjax, 'plugin.mathjax', props => {
|
||||
if (props.head) {
|
||||
const { head, helper } = props;
|
||||
if (head) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
jsUrl: props.cdn('mathjax', '2.7.5', 'unpacked/MathJax.js?config=TeX-MML-AM_CHTML')
|
||||
jsUrl: helper.cdn('mathjax', '2.7.5', 'unpacked/MathJax.js?config=TeX-MML-AM_CHTML')
|
||||
};
|
||||
});
|
||||
|
|
|
@ -35,9 +35,10 @@ class OutdatedBrowser extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(OutdatedBrowser, 'plugin.outdatedbrowser', props => {
|
||||
const { head, helper } = props;
|
||||
return {
|
||||
head: props.head,
|
||||
cssUrl: props.cdn('outdatedbrowser', '1.1.5', 'outdatedbrowser/outdatedbrowser.min.css'),
|
||||
jsUrl: props.cdn('outdatedbrowser', '1.1.5', 'outdatedbrowser/outdatedbrowser.min.js')
|
||||
head,
|
||||
cssUrl: helper.cdn('outdatedbrowser', '1.1.5', 'outdatedbrowser/outdatedbrowser.min.css'),
|
||||
jsUrl: helper.cdn('outdatedbrowser', '1.1.5', 'outdatedbrowser/outdatedbrowser.min.js')
|
||||
};
|
||||
});
|
||||
|
|
|
@ -15,11 +15,12 @@ class ProgressBar extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(ProgressBar, 'plugin.progressbar', props => {
|
||||
if (!props.head) {
|
||||
const { head, helper } = props;
|
||||
if (!head) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
url_for: props.url_for,
|
||||
jsUrl: props.cdn('pace-js', '1.0.2', 'pace.min.js')
|
||||
url_for: helper.url_for,
|
||||
jsUrl: helper.cdn('pace-js', '1.0.2', 'pace.min.js')
|
||||
};
|
||||
});
|
||||
|
|
|
@ -12,17 +12,17 @@ class Archives extends Component {
|
|||
} = this.props;
|
||||
|
||||
return <div className="card widget">
|
||||
<div class="card-content">
|
||||
<div class="menu">
|
||||
<h3 class="menu-label">{title}</h3>
|
||||
<ul class="menu-list">
|
||||
<div className="card-content">
|
||||
<div className="menu">
|
||||
<h3 className="menu-label">{title}</h3>
|
||||
<ul className="menu-list">
|
||||
{items.map(archive => <li>
|
||||
<a class="level is-marginless" href={archive.url}>
|
||||
<span class="level-start">
|
||||
<span class="level-item">{archive.name}</span>
|
||||
<a className="level is-marginless" href={archive.url}>
|
||||
<span className="level-start">
|
||||
<span className="level-item">{archive.name}</span>
|
||||
</span>
|
||||
{showCount ? <span class="level-end">
|
||||
<span class="level-item tag">{archive.count}</span>
|
||||
{showCount ? <span className="level-end">
|
||||
<span className="level-item tag">{archive.count}</span>
|
||||
</span> : null}
|
||||
</a>
|
||||
</li>)}
|
||||
|
@ -35,15 +35,23 @@ class Archives extends Component {
|
|||
|
||||
module.exports = cacheComponent(Archives, 'widget.archives', props => {
|
||||
// adapted from hexo/lib/plugins/helper/list_archives.js
|
||||
const { config, page, type = 'monthly', order = -1, url_for, _p } = props;
|
||||
const posts = props.site.posts.sort('date', order);
|
||||
const {
|
||||
site,
|
||||
config,
|
||||
page,
|
||||
helper,
|
||||
type = 'monthly',
|
||||
order = -1,
|
||||
show_count = true,
|
||||
format = null
|
||||
} = props;
|
||||
const { url_for, _p } = helper;
|
||||
const posts = site.posts.sort('date', order);
|
||||
if (!posts.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const language = page.lang || page.language || config.language;
|
||||
const format = props.format || type === 'monthly' ? 'MMMM YYYY' : 'YYYY';
|
||||
const showCount = Object.prototype.hasOwnProperty.call(props, 'show_count') ? props.show_count : true;
|
||||
|
||||
const data = [];
|
||||
let length = 0;
|
||||
|
@ -61,7 +69,7 @@ module.exports = cacheComponent(Archives, 'widget.archives', props => {
|
|||
|
||||
const year = date.year();
|
||||
const month = date.month() + 1;
|
||||
const name = date.format(format);
|
||||
const name = date.format(format || type === 'monthly' ? 'MMMM YYYY' : 'YYYY');
|
||||
const lastData = data[length - 1];
|
||||
|
||||
if (!lastData || lastData.name !== name) {
|
||||
|
@ -94,6 +102,6 @@ module.exports = cacheComponent(Archives, 'widget.archives', props => {
|
|||
url: link(item)
|
||||
})),
|
||||
title: _p('common.archive', Infinity),
|
||||
showCount
|
||||
showCount: show_count
|
||||
};
|
||||
});
|
||||
|
|
|
@ -6,12 +6,12 @@ const { cacheComponent } = require('../util/cache');
|
|||
class Categories extends Component {
|
||||
renderList(categories, showCount) {
|
||||
return categories.map(category => <li>
|
||||
<a class="level is-marginless" href={category.url}>
|
||||
<span class="level-start">
|
||||
<span class="level-item">{category.name}</span>
|
||||
<a className="level is-marginless" href={category.url}>
|
||||
<span className="level-start">
|
||||
<span className="level-item">{category.name}</span>
|
||||
</span>
|
||||
{showCount ? <span class="level-end">
|
||||
<span class="level-item tag">{category.count}</span>
|
||||
{showCount ? <span className="level-end">
|
||||
<span className="level-item tag">{category.count}</span>
|
||||
</span> : null}
|
||||
</a>
|
||||
{category.children.length ? <ul>{this.renderList(category.children)}</ul> : null}
|
||||
|
@ -26,10 +26,10 @@ class Categories extends Component {
|
|||
} = this.props;
|
||||
|
||||
return <div className="card widget">
|
||||
<div class="card-content">
|
||||
<div class="menu">
|
||||
<h3 class="menu-label">{title}</h3>
|
||||
<ul class="menu-list">
|
||||
<div className="card-content">
|
||||
<div className="menu">
|
||||
<h3 className="menu-label">{title}</h3>
|
||||
<ul className="menu-list">
|
||||
{this.renderList(categories, showCount)}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -40,14 +40,25 @@ class Categories extends Component {
|
|||
|
||||
module.exports = cacheComponent(Categories, 'widget.categories', props => {
|
||||
// adapted from hexo/lib/plugins/helper/list_categories.js
|
||||
const categories = props.categories || props.site.categories;
|
||||
if (!categories.length) {
|
||||
const {
|
||||
page,
|
||||
helper,
|
||||
categories = props.site.categories,
|
||||
orderBy = 'name',
|
||||
order = 1,
|
||||
show_current = false,
|
||||
show_count = true
|
||||
} = props;
|
||||
const { url_for, _p } = helper;
|
||||
|
||||
if (!categories || !categories.length) {
|
||||
return null;
|
||||
}
|
||||
const { orderBy = 'name', order = 1, page, url_for, _p } = props;
|
||||
const depth = props.depth ? parseInt(props.depth, 10) : 0;
|
||||
const showCurrent = props.show_current || false;
|
||||
const showCount = Object.prototype.hasOwnProperty.call(props, 'show_count') ? props.show_count : true;
|
||||
|
||||
let depth = 0;
|
||||
try {
|
||||
depth = parseInt(props.depth, 10);
|
||||
} catch (e) { }
|
||||
|
||||
function prepareQuery(parent) {
|
||||
const query = {};
|
||||
|
@ -69,7 +80,7 @@ module.exports = cacheComponent(Categories, 'widget.categories', props => {
|
|||
}
|
||||
|
||||
let isCurrent = false;
|
||||
if (showCurrent && page) {
|
||||
if (show_current && page) {
|
||||
for (let j = 0; j < cat.length; j++) {
|
||||
const post = cat.posts.data[j];
|
||||
if (post && post._id === page._id) {
|
||||
|
@ -92,7 +103,7 @@ module.exports = cacheComponent(Categories, 'widget.categories', props => {
|
|||
}
|
||||
|
||||
return {
|
||||
showCount,
|
||||
showCount: show_count,
|
||||
categories: hierarchicalList(0),
|
||||
title: _p('common.category', Infinity)
|
||||
};
|
||||
|
|
|
@ -7,23 +7,23 @@ const { cacheComponent } = require('../util/cache');
|
|||
class Links extends Component {
|
||||
render() {
|
||||
const { title, links } = this.props;
|
||||
return <div class="card widget">
|
||||
<div class="card-content">
|
||||
<div class="menu">
|
||||
<h3 class="menu-label">{title}</h3>
|
||||
<ul class="menu-list">
|
||||
return <div className="card widget">
|
||||
<div className="card-content">
|
||||
<div className="menu">
|
||||
<h3 className="menu-label">{title}</h3>
|
||||
<ul className="menu-list">
|
||||
{Object.keys(links).map(i => {
|
||||
let hostname = links[i];
|
||||
try {
|
||||
hostname = new URL(hostname).hostname;
|
||||
} catch (e) { }
|
||||
return <li>
|
||||
<a class="level is-mobile" href="<%- links[i] %>" target="_blank" rel="noopener">
|
||||
<span class="level-left">
|
||||
<span class="level-item">{i}</span>
|
||||
<a className="level is-mobile" href="<%- links[i] %>" target="_blank" rel="noopener">
|
||||
<span className="level-left">
|
||||
<span className="level-item">{i}</span>
|
||||
</span>
|
||||
<span class="level-right">
|
||||
<span class="level-item tag">{hostname}</span>
|
||||
<span className="level-right">
|
||||
<span className="level-item tag">{hostname}</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>;
|
||||
|
@ -36,11 +36,12 @@ class Links extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(Links, 'widget.links', props => {
|
||||
if (!Object.keys(props.links).length) {
|
||||
const { helper, widget } = props;
|
||||
if (!Object.keys(widget.links).length) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
title: props.__('widget.links'),
|
||||
links: props.links
|
||||
title: helper.__('widget.links'),
|
||||
links: widget.links
|
||||
};
|
||||
});
|
||||
|
|
|
@ -9,11 +9,11 @@ class Profile extends Component {
|
|||
if (!links.length) {
|
||||
return null;
|
||||
}
|
||||
return <div class="level is-mobile">
|
||||
return <div className="level is-mobile">
|
||||
{links.map(link => {
|
||||
return <a class="level-item button is-white is-marginless"
|
||||
return <a className="level-item button is-white is-marginless"
|
||||
target="_blank" rel="noopener" title={link.name} href={link.url}>
|
||||
{Object.prototype.hasOwnProperty.call(link, 'icon') ? <i class={link.icon}></i> : link.name}
|
||||
{Object.prototype.hasOwnProperty.call(link, 'icon') ? <i className={link.icon}></i> : link.name}
|
||||
</a>;
|
||||
})}
|
||||
</div>;
|
||||
|
@ -31,51 +31,51 @@ class Profile extends Component {
|
|||
followTitle,
|
||||
socialLinks
|
||||
} = this.props;
|
||||
return <div class="card widget">
|
||||
<div class="card-content">
|
||||
<nav class="level">
|
||||
<div class="level-item has-text-centered" style="flex-shrink: 1">
|
||||
return <div className="card widget">
|
||||
<div className="card-content">
|
||||
<nav className="level">
|
||||
<div className="level-item has-text-centered" style="flex-shrink: 1">
|
||||
<div>
|
||||
<figure class="image is-128x128 has-mb-6">
|
||||
<img class={avatarRounded ? 'is-rounded' : ''} src={avatar} alt={author} />
|
||||
<figure className="image is-128x128 has-mb-6">
|
||||
<img className={avatarRounded ? 'is-rounded' : ''} src={avatar} alt={author} />
|
||||
</figure>
|
||||
{author ? <p class="is-size-4 is-block">{author}</p> : null}
|
||||
{authorTitle ? <p class="is-size-6 is-block">{authorTitle}</p> : null}
|
||||
{location ? <p class="is-size-6 is-flex is-flex-center has-text-grey">
|
||||
<i class="fas fa-map-marker-alt has-mr-7"></i>
|
||||
{author ? <p className="is-size-4 is-block">{author}</p> : null}
|
||||
{authorTitle ? <p className="is-size-6 is-block">{authorTitle}</p> : null}
|
||||
{location ? <p className="is-size-6 is-flex is-flex-center has-text-grey">
|
||||
<i className="fas fa-map-marker-alt has-mr-7"></i>
|
||||
<span>{location}</span>
|
||||
</p> : null}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="level is-mobile">
|
||||
<div class="level-item has-text-centered is-marginless">
|
||||
<nav className="level is-mobile">
|
||||
<div className="level-item has-text-centered is-marginless">
|
||||
<div>
|
||||
<p class="heading">{counter.post.title}</p>
|
||||
<p className="heading">{counter.post.title}</p>
|
||||
<a href={counter.post.url}>
|
||||
<p class="title has-text-weight-normal">{counter.post.count}</p>
|
||||
<p className="title has-text-weight-normal">{counter.post.count}</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item has-text-centered is-marginless">
|
||||
<div className="level-item has-text-centered is-marginless">
|
||||
<div>
|
||||
<p class="heading">{counter.category.title}</p>
|
||||
<p className="heading">{counter.category.title}</p>
|
||||
<a href={counter.category.url}>
|
||||
<p class="title has-text-weight-normal">{counter.category.count}</p>
|
||||
<p className="title has-text-weight-normal">{counter.category.count}</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item has-text-centered is-marginless">
|
||||
<div className="level-item has-text-centered is-marginless">
|
||||
<div>
|
||||
<p class="heading">{counter.tag.title}</p>
|
||||
<p className="heading">{counter.tag.title}</p>
|
||||
<a href={counter.tag.url}>
|
||||
<p class="title has-text-weight-normal">{counter.tag.count}</p>
|
||||
<p className="title has-text-weight-normal">{counter.tag.count}</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{followLink ? <div class="level">
|
||||
<a class="level-item button is-link is-rounded" href={followLink} target="_blank" rel="noopener">{followTitle}</a>
|
||||
{followLink ? <div className="level">
|
||||
<a className="level-item button is-link is-rounded" href={followLink} target="_blank" rel="noopener">{followTitle}</a>
|
||||
</div> : null}
|
||||
{this.renderSocialLinks(socialLinks)}
|
||||
</div>
|
||||
|
@ -84,6 +84,7 @@ class Profile extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(Profile, 'widget.profile', props => {
|
||||
const { site, helper, widget } = props;
|
||||
const {
|
||||
avatar,
|
||||
gravatar,
|
||||
|
@ -92,12 +93,9 @@ module.exports = cacheComponent(Profile, 'widget.profile', props => {
|
|||
author_title,
|
||||
location,
|
||||
follow_link,
|
||||
social_links,
|
||||
site,
|
||||
url_for,
|
||||
_p,
|
||||
__
|
||||
} = props;
|
||||
social_links
|
||||
} = widget;
|
||||
const { url_for, _p, __ } = helper;
|
||||
|
||||
function getAvatar() {
|
||||
if (gravatar) {
|
||||
|
@ -126,7 +124,6 @@ module.exports = cacheComponent(Profile, 'widget.profile', props => {
|
|||
url: url_for(link.name),
|
||||
icon: link.icon
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -7,28 +7,28 @@ class RecentPosts extends Component {
|
|||
render() {
|
||||
const { title, thumbnail, posts } = this.props;
|
||||
|
||||
return <div class="card widget">
|
||||
<div class="card-content">
|
||||
<h3 class="menu-label">{title}</h3>
|
||||
return <div className="card widget">
|
||||
<div className="card-content">
|
||||
<h3 className="menu-label">{title}</h3>
|
||||
{posts.map(post => {
|
||||
const categories = [];
|
||||
post.categories.forEach((category, i) => {
|
||||
categories.push(<a class="has-link-grey" href={category.url}>{category.name}</a>);
|
||||
categories.push(<a className="has-link-grey" href={category.url}>{category.name}</a>);
|
||||
if (i < post.categories.length - 1) {
|
||||
categories.push('/');
|
||||
}
|
||||
});
|
||||
return <article class="media">
|
||||
{thumbnail ? <a href={post.url} class="media-left">
|
||||
<p class="image is-64x64">
|
||||
<img class="thumbnail" src={post.thumbnail} alt={post.title} />
|
||||
return <article className="media">
|
||||
{thumbnail ? <a href={post.url} className="media-left">
|
||||
<p className="image is-64x64">
|
||||
<img className="thumbnail" src={post.thumbnail} alt={post.title} />
|
||||
</p>
|
||||
</a> : null}
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<div><time class="has-text-grey is-size-7 is-uppercase" datetime={post.dateXml}>{post.date}</time></div>
|
||||
<a href={post.url} class="title has-link-black-ter is-size-6 has-text-weight-normal">{post.title}</a>
|
||||
<p class="is-size-7 is-uppercase">{categories}</p>
|
||||
<div className="media-content">
|
||||
<div className="content">
|
||||
<div><time className="has-text-grey is-size-7 is-uppercase" datetime={post.dateXml}>{post.date}</time></div>
|
||||
<a href={post.url} className="title has-link-black-ter is-size-6 has-text-weight-normal">{post.title}</a>
|
||||
<p className="is-size-7 is-uppercase">{categories}</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>;
|
||||
|
@ -39,7 +39,8 @@ class RecentPosts extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(RecentPosts, 'widget.recentposts', props => {
|
||||
const { site, config, get_thumbnail, url_for, __, date_xml, date } = props;
|
||||
const { site, config, helper } = props;
|
||||
const { get_thumbnail, url_for, __, date_xml, date } = helper;
|
||||
if (!site.posts.length) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -7,27 +7,27 @@ class SubscribeEmail extends Component {
|
|||
render() {
|
||||
const { title, description, feedburnerId, buttonTitle } = this.props;
|
||||
|
||||
return <div class="card widget">
|
||||
<div class="card-content">
|
||||
<div class="menu">
|
||||
<h3 class="menu-label">{title}</h3>
|
||||
return <div className="card widget">
|
||||
<div className="card-content">
|
||||
<div className="menu">
|
||||
<h3 className="menu-label">{title}</h3>
|
||||
<div>
|
||||
<form action="https://feedburner.google.com/fb/a/mailverify" method="post" target="popupwindow"
|
||||
onsubmit={`window.open('https://feedburner.google.com/fb/a/mailverify?uri=${feedburnerId}','popupwindow','scrollbars=yes,width=550,height=520');return true`}>
|
||||
<input type="hidden" value={feedburnerId} name="uri" />
|
||||
<input type="hidden" name="loc" value="en_US" />
|
||||
<div class="field">
|
||||
<div class="control has-icons-left">
|
||||
<input class="input" name="email" type="email" />
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-envelope"></i>
|
||||
<div className="field">
|
||||
<div className="control has-icons-left">
|
||||
<input className="input" name="email" type="email" />
|
||||
<span className="icon is-small is-left">
|
||||
<i className="fas fa-envelope"></i>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help">{description}</p>
|
||||
<p className="help">{description}</p>
|
||||
</div>
|
||||
<div class="field is-grouped is-grouped-right">
|
||||
<div class="control">
|
||||
<input class="button is-link" type="submit" value={buttonTitle} />
|
||||
<div className="field is-grouped is-grouped-right">
|
||||
<div className="control">
|
||||
<input className="button is-link" type="submit" value={buttonTitle} />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -39,12 +39,13 @@ class SubscribeEmail extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(SubscribeEmail, 'widget.subscribeemail', props => {
|
||||
const { feedburner_id, description, __ } = props;
|
||||
const { helper, widget } = props;
|
||||
const { feedburner_id, description } = widget;
|
||||
|
||||
return {
|
||||
title: __('widget.email.title'),
|
||||
feedburnerId: feedburner_id,
|
||||
description,
|
||||
buttonTitle: __('widget.email.button')
|
||||
feedburnerId: feedburner_id,
|
||||
title: helper.__('widget.email.title'),
|
||||
buttonTitle: helper.__('widget.email.button')
|
||||
};
|
||||
});
|
||||
|
|
|
@ -12,14 +12,14 @@ class Tags extends Component {
|
|||
} = this.props;
|
||||
|
||||
return <div className="card widget">
|
||||
<div class="card-content">
|
||||
<div class="menu">
|
||||
<h3 class="menu-label">{title}</h3>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
{tags.map(tag => <div class="control">
|
||||
<a class="tags has-addons" href={tag.url}>
|
||||
<span class="tag">{tag.name}</span>
|
||||
{showCount ? <span class="tag is-grey">{tag.count}</span> : null}
|
||||
<div className="card-content">
|
||||
<div className="menu">
|
||||
<h3 className="menu-label">{title}</h3>
|
||||
<div className="field is-grouped is-grouped-multiline">
|
||||
{tags.map(tag => <div className="control">
|
||||
<a className="tags has-addons" href={tag.url}>
|
||||
<span className="tag">{tag.name}</span>
|
||||
{showCount ? <span className="tag is-grey">{tag.count}</span> : null}
|
||||
</a>
|
||||
</div>)}
|
||||
</div>
|
||||
|
@ -31,12 +31,19 @@ class Tags extends Component {
|
|||
|
||||
module.exports = cacheComponent(Tags, 'widget.tags', props => {
|
||||
// adapted from hexo/lib/plugins/helper/list_tags.js
|
||||
const {
|
||||
helper,
|
||||
orderBy = 'name',
|
||||
order = 1,
|
||||
amount,
|
||||
show_count = true
|
||||
} = props;
|
||||
let tags = props.tags || props.site.tags;
|
||||
if (!tags.length) {
|
||||
const { url_for, _p } = helper;
|
||||
|
||||
if (!tags || !tags.length) {
|
||||
return null;
|
||||
}
|
||||
const { orderBy = 'name', order = 1, amount, url_for, _p } = props;
|
||||
const showCount = Object.prototype.hasOwnProperty.call(props, 'show_count') ? props.show_count : true;
|
||||
|
||||
tags = tags.sort(orderBy, order).filter(tag => tag.length);
|
||||
if (amount) {
|
||||
|
@ -44,7 +51,7 @@ module.exports = cacheComponent(Tags, 'widget.tags', props => {
|
|||
}
|
||||
|
||||
return {
|
||||
showCount,
|
||||
showCount: show_count,
|
||||
title: _p('common.tag', Infinity),
|
||||
tags: tags.map(tag => ({
|
||||
name: tag.name,
|
||||
|
|
|
@ -85,7 +85,7 @@ class Toc extends Component {
|
|||
.sort((a, b) => a - b);
|
||||
|
||||
if (keys.length > 0) {
|
||||
result = <ul class="menu-list">
|
||||
result = <ul className="menu-list">
|
||||
{keys.map(i => this.renderToc(toc[i]))}
|
||||
</ul>;
|
||||
}
|
||||
|
@ -93,8 +93,8 @@ class Toc extends Component {
|
|||
&& Object.prototype.hasOwnProperty.call(toc, 'index')
|
||||
&& Object.prototype.hasOwnProperty.call(toc, 'text')) {
|
||||
result = <li>
|
||||
<a class="is-flex" href={'#' + toc.id}>
|
||||
<span class="has-mr-6">{toc.index}</span>
|
||||
<a className="is-flex" href={'#' + toc.id}>
|
||||
<span className="has-mr-6">{toc.index}</span>
|
||||
<span>{toc.text}</span>
|
||||
</a>
|
||||
{result}
|
||||
|
@ -109,10 +109,10 @@ class Toc extends Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
return <div class="card widget" id="toc">
|
||||
<div class="card-content">
|
||||
<div class="menu">
|
||||
<h3 class="menu-label">{this.props.title}</h3>
|
||||
return <div className="card widget" id="toc">
|
||||
<div className="card-content">
|
||||
<div className="menu">
|
||||
<h3 className="menu-label">{this.props.title}</h3>
|
||||
{this.renderToc(toc)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -121,15 +121,15 @@ class Toc extends Component {
|
|||
}
|
||||
|
||||
module.exports = cacheComponent(Toc, 'widget.toc', props => {
|
||||
const { toc, page, _p } = props;
|
||||
const { config, page, helper } = props;
|
||||
const { layout, content } = page;
|
||||
|
||||
if (toc !== true || (layout !== 'page' && layout !== 'post')) {
|
||||
if (config.toc !== true || (layout !== 'page' && layout !== 'post')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
title: _p('widget.catalogue', Infinity),
|
||||
title: helper._p('widget.catalogue', Infinity),
|
||||
content
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue