linkcard
This commit is contained in:
parent
c1183fd0c1
commit
05564d4bbc
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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">';
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
.title
|
||||||
|
display: -webkit-box
|
||||||
|
-webkit-box-orient: vertical
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
|
-webkit-line-clamp: 2
|
||||||
|
.cap
|
||||||
flex-shrink: 0
|
flex-shrink: 0
|
||||||
>.left
|
color: var(--text-p3)
|
||||||
|
.link
|
||||||
|
line-height: 1.5
|
||||||
|
opacity: .75
|
||||||
|
txt-ellipsis()
|
||||||
|
.desc
|
||||||
|
display: -webkit-box
|
||||||
|
-webkit-box-orient: vertical
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
margin: .5rem 0 .5rem 1rem
|
-webkit-line-clamp: 3
|
||||||
.title
|
.img
|
||||||
font-size: $fs-14
|
border-radius: 4px
|
||||||
margin: 1px 0 5px 0
|
|
||||||
.desc
|
|
||||||
flex-shrink: 0
|
|
||||||
txt-ellipsis()
|
|
||||||
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)
|
|
||||||
|
|
|
@ -217,14 +217,20 @@ 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];
|
||||||
const els = document.getElementsByClassName('stellar-' + key + '-api');
|
if (key == 'linkcard') {
|
||||||
if (els != undefined && els.length > 0) {
|
stellar.loadScript(js, { defer: true }).then(function () {
|
||||||
stellar.jQuery(() => {
|
setCardLink(document.querySelectorAll('a.link-card'));
|
||||||
stellar.loadScript(js, { defer: true });
|
});
|
||||||
if (key == 'timeline') {
|
} else {
|
||||||
stellar.loadScript(stellar.plugins.marked);
|
const els = document.getElementsByClassName('stellar-' + key + '-api');
|
||||||
}
|
if (els != undefined && els.length > 0) {
|
||||||
})
|
stellar.jQuery(() => {
|
||||||
|
stellar.loadScript(js, { defer: true });
|
||||||
|
if (key == 'timeline') {
|
||||||
|
stellar.loadScript(stellar.plugins.marked);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue