This commit is contained in:
xaoxuu 2022-10-25 22:41:56 +08:00
parent c1183fd0c1
commit 05564d4bbc
6 changed files with 186 additions and 46 deletions

View File

@ -250,7 +250,7 @@ plugins:
friends: /js/plugins/friends.js friends: /js/plugins/friends.js
ghinfo: /js/plugins/ghinfo.js ghinfo: /js/plugins/ghinfo.js
timeline: /js/plugins/timeline.js timeline: /js/plugins/timeline.js
linkcard: /js/plugins/linkcard.js
marked: https://cdn.bootcdn.net/ajax/libs/marked/4.0.18/marked.min.js marked: https://cdn.bootcdn.net/ajax/libs/marked/4.0.18/marked.min.js

View File

@ -2,7 +2,7 @@
* link.js v1.1 | https://github.com/xaoxuu/hexo-theme-stellar/ * link.js v1.1 | https://github.com/xaoxuu/hexo-theme-stellar/
* 格式与官方标签插件一致使用空格分隔中括号内的是可选参数中括号不需要写出来 * 格式与官方标签插件一致使用空格分隔中括号内的是可选参数中括号不需要写出来
* *
* {% link url title [description] [icon:src] %} * {% link url [title] [desc:true/false] [icon:src] %}
*/ */
'use strict'; 'use strict';
@ -11,11 +11,11 @@ var util = require('hexo-util');
const full_url_for = require('hexo-util').full_url_for.bind(hexo); const full_url_for = require('hexo-util').full_url_for.bind(hexo);
hexo.extend.tag.register('link', function(args) { hexo.extend.tag.register('link', function(args) {
args = hexo.args.map(args, ['icon'], ['url', 'title', 'description']); args = hexo.args.map(args, ['icon', 'desc'], ['url', 'title']);
var el = ''; var el = '';
el += '<div class="tag-plugin link dis-select">'; el += '<div class="tag-plugin link dis-select">';
el += '<a class="link-card' + (args.description ? ' rich' : ' plain') + '" title="' + args.title + '" href="' + args.url + '"'; el += '<a class="link-card' + (args.desc ? ' rich' : ' plain') + '" title="' + args.title + '" href="' + args.url + '"';
if (args.url.includes('://')) { if (args.url.includes('://')) {
el += ' target="_blank" rel="external nofollow noopener noreferrer"'; el += ' target="_blank" rel="external nofollow noopener noreferrer"';
} }
@ -31,17 +31,19 @@ hexo.extend.tag.register('link', function(args) {
return el; return el;
} }
function loadTitle() { function loadTitle() {
return '<span class="title">' + args.title + '</span>'; return '<span class="title">' + (args.title || args.url) + '</span>';
} }
function loadDesc() { function loadDesc() {
return '<span class="desc fs12">' + (args.description || full_url_for(args.url)) + '</span>'; return '<span class="cap desc fs12"></span>';
}
function loadLink() {
return '<span class="cap link fs12">' + full_url_for(args.url) + '</span>';
} }
if (args.description) { if (args.desc) {
// top // top
el += '<div class="top">'; el += '<div class="top">';
el += loadIcon(); el += loadIcon() + loadLink();
el += '<span class="desc fs12">' + full_url_for(args.url) + '</span>';
el += '</div>'; el += '</div>';
// bottom // bottom
el += '<div class="bottom">'; el += '<div class="bottom">';
@ -50,7 +52,7 @@ hexo.extend.tag.register('link', function(args) {
} else { } else {
// left // left
el += '<div class="left">'; el += '<div class="left">';
el += loadTitle() + loadDesc(); el += loadTitle() + loadLink();
el += '</div>'; el += '</div>';
// right // right
el += '<div class="right">'; el += '<div class="right">';

View File

@ -12,7 +12,7 @@ a
&:hover &:hover
color: $color-hover color: $color-hover
p:not([class]) .md p:not([class])
text-align: convert(hexo-config('style.text-align')) text-align: convert(hexo-config('style.text-align'))
hr hr

View File

@ -14,8 +14,8 @@
cursor: pointer cursor: pointer
min-width 280px min-width 280px
max-width: 100% max-width: 100%
width: 320px width: 350px
@media screen and (max-width: $device-mobile-375) @media screen and (max-width: $device-mobile-425)
width: 100% width: 100%
box-shadow: $boxshadow-card box-shadow: $boxshadow-card
border-radius: $border-block border-radius: $border-block
@ -35,54 +35,68 @@
text-align: justify text-align: justify
.md .link-card .md .link-card
line-height: 1.2 >.left
> div overflow: hidden
pointer-events: none margin: 0.75rem 0 0.75rem 0.75rem
.title
font-size: $fs-14
span+span
margin-top: 0.25rem
>.right
width: 2.75rem
height: 2.75rem
margin: 0.75rem
overflow: hidden
flex-shrink: 0
>.top >.top
display: flex display: flex
margin: .75rem 1rem .25rem margin: .75rem 0.75rem .25rem
overflow: hidden overflow: hidden
max-width: 'calc(100% - %s * 2)' % 1rem max-width: 'calc(100% - %s * 2)' % 1rem
align-items: center align-items: center
.img .img
line-height: 0 line-height: 0
height: 16px height: 16px
width: 20px width: 16px
margin-right: 4px border-radius: 16px
margin-right: 8px
background-repeat: no-repeat background-repeat: no-repeat
background-size: contain background-size: contain
background-position: left center background-position: left center
.desc
opacity: .75
line-height: 1.5
span span
txt-ellipsis() txt-ellipsis()
max-width: 100% max-width: 100%
>.bottom >.bottom
margin: 0 1rem .75rem 1rem margin: 0 0.75rem 0.75rem 0.75rem
.title .title
font-size: $fs-15 font-size: $fs-15
margin: 4px 0 8px 0 margin: 4px 0 8px 0
>.right
width: 2.5rem .md .link-card
height: 2.5rem line-height: 1.2
margin: .75rem
overflow: hidden
flex-shrink: 0
>.left
overflow: hidden
margin: .5rem 0 .5rem 1rem
.title .title
font-size: $fs-14 display: -webkit-box
margin: 1px 0 5px 0 -webkit-box-orient: vertical
.desc overflow: hidden
-webkit-line-clamp: 2
.cap
flex-shrink: 0 flex-shrink: 0
color: var(--text-p3)
.link
line-height: 1.5
opacity: .75
txt-ellipsis() txt-ellipsis()
.desc
display: -webkit-box
-webkit-box-orient: vertical
overflow: hidden
-webkit-line-clamp: 3
.img
border-radius: 4px
span span
margin: 0 margin: 0
display: block display: block
span.title .title
font-weight: 500 font-weight: 500
color: var(--text-p1) color: var(--text-p1)
span.desc
color: var(--text-p3)

View File

@ -217,6 +217,11 @@ if (stellar.plugins.lazyload) {
if (stellar.plugins.stellar) { if (stellar.plugins.stellar) {
for (let key of Object.keys(stellar.plugins.stellar)) { for (let key of Object.keys(stellar.plugins.stellar)) {
let js = stellar.plugins.stellar[key]; let js = stellar.plugins.stellar[key];
if (key == 'linkcard') {
stellar.loadScript(js, { defer: true }).then(function () {
setCardLink(document.querySelectorAll('a.link-card'));
});
} else {
const els = document.getElementsByClassName('stellar-' + key + '-api'); const els = document.getElementsByClassName('stellar-' + key + '-api');
if (els != undefined && els.length > 0) { if (els != undefined && els.length > 0) {
stellar.jQuery(() => { stellar.jQuery(() => {
@ -228,6 +233,7 @@ if (stellar.plugins.stellar) {
} }
} }
} }
}
// swiper // swiper
if (stellar.plugins.swiper) { if (stellar.plugins.swiper) {

View File

@ -0,0 +1,118 @@
// 本插件由CardLink定制而成原项目源码: https://github.com/Lete114/CardLink
var cardLink = {};
cardLink.caches = {};
cardLink.server = 'https://api.allorigins.win/raw?url=';
/**
* Remove '/' and '/index.html'
* @param {String} params
* @returns { String }
*/
function indexHandler(params) {
let path = params.replace(/(\/index\.html|\/)*$/gi, '')
if (path.length === 0) path += '/'
return path
}
/**
* Determine if it is a ['https://', 'http://', '//'] protocol
* @param {String} url Website url
* @returns {Boolean}
*/
function isHttp(url) {
return /^(https?:)?\/\//g.test(url)
}
function renderer(el, obj) {
el.querySelector('.title').innerHTML = obj.title;
if (obj.icon && obj.icon.length > 0) {
el.querySelector('.img').style = 'background-image: url("' + obj.icon + '");';
}
if (obj.desc && obj.desc.length > 0) {
el.querySelector('.desc').innerHTML = obj.desc;
}
}
/**
* Get info
* @param {Element} el Element
* @param {String} html String type html
* @param {String} link Website address
*/
// eslint-disable-next-line max-statements
function getInfo(el, html, link) {
try {
let title, icon, desc
const doc = new DOMParser().parseFromString(html, 'text/html')
// If there is no title, no card link is generated
title = doc.querySelector('title')
if (title) {
title = title.textContent
// Get the src of the first img tag in the body tag
// icon = doc.querySelector('body img')
// icon = icon && icon.getAttribute('src')
if (/^data:image/.test(icon)) icon = ''
// If there is no src then get the site icon
if (!icon) {
const links = [].slice.call(doc.querySelectorAll('link[rel][href]'))
icon = links.find((_el) => _el.rel.includes('icon'))
icon = icon && icon.getAttribute('href')
}
desc = doc.querySelector('head meta[name="description"]')
if (desc) {
desc = desc.content;
}
// If `icon` is not the ['https://', 'http://', '//'] protocol, splice on the `origin` of the a tag
if (icon && !isHttp(icon)) icon = new URL(link).origin + icon
cardLink.caches[link] = { title, link, icon, desc }
renderer(el, cardLink.caches[link])
}
} catch (error) {
// eslint-disable-next-line no-console
console.warn('CardLink Error: Failed to parse', error)
}
}
function fetchPage(link, callback) {
fetch(link)
.then((result) => result.text())
.then(callback)
.catch((error) => {
const server = cardLink.server
// eslint-disable-next-line no-console
if (link.includes(server) || !server) return console.error('CardLink Error:', error)
fetchPage(server + link, callback)
})
}
/**
* Create card links
* @param {NodeList} nodes A collection of nodes or a collection of arrays,
* if it is an array then the array must always contain node element
*/
function setCardLink(nodes) {
// If the `nodes` do not contain a `forEach` method, then the default `a[cardlink]` is used
nodes = 'forEach' in (nodes || {}) ? nodes : document.querySelectorAll('a[cardlink]')
nodes.forEach((el) => {
// If it is not a tag element then it is not processed
if (el.nodeType !== 1) return
el.removeAttribute('cardlink')
const link = el.href
const cache = cardLink.caches[link]
if (cache) return renderer(el, cache)
if (isHttp(link)) {
fetchPage(link, (html) => {
getInfo(el, html, link)
})
}
})
}