hexo-filter-github-emojis/index.js

145 lines
4.1 KiB
JavaScript
Raw Normal View History

2016-12-22 19:11:28 +08:00
'use strict'
/* global hexo */
2018-10-04 19:07:24 +08:00
const _ = require('lodash')
const path = require('path')
const fs = require('fs')
2019-08-15 01:42:50 +08:00
const { JSDOM } = require('jsdom')
2016-12-22 19:11:28 +08:00
var options = _.assign({
enable: true,
2018-10-04 19:07:24 +08:00
inject: true,
2016-12-23 18:46:01 +08:00
version: 'latest',
2018-10-04 19:07:24 +08:00
className: 'github-emoji',
2016-12-22 19:11:28 +08:00
}, hexo.config.githubEmojis)
if (options.enable !== false) {
2018-10-04 19:07:24 +08:00
const emojis = _.assign(
{},
require('./emojis.json'),
loadCustomEmojis(options.customEmojis || options.localEmojis),
)
2016-12-22 19:11:28 +08:00
2018-10-04 19:07:24 +08:00
fs.writeFile(
path.join(__dirname, 'emojis.json'),
JSON.stringify(emojis, null, ' '),
function (err) { err && console.warn(err) },
)
2016-12-22 19:11:28 +08:00
2018-10-04 19:07:24 +08:00
hexo.extend.filter.register('after_post_render', data => {
if (!options.inject && data['no-emoji']) { return data }
2019-08-15 01:42:50 +08:00
const $content = new JSDOM(data.content)
const $excerpt = new JSDOM(data.excerpt)
2018-10-04 19:07:24 +08:00
if (options.inject) {
2019-08-15 01:42:50 +08:00
const $script = $content.window.document.createElement('script')
$script.innerHTML = `
2018-10-04 19:07:24 +08:00
document.querySelectorAll('.${options.className}')
.forEach(el => {
if (!el.dataset.src) { return; }
const img = document.createElement('img');
img.style = 'display:none !important;';
img.src = el.dataset.src;
img.addEventListener('error', () => {
img.remove();
el.style.color = 'inherit';
el.style.backgroundImage = 'none';
el.style.background = 'none';
});
img.addEventListener('load', () => {
img.remove();
});
document.body.appendChild(img);
});
2019-08-15 01:42:50 +08:00
`
$content.window.document.body.appendChild($script)
2018-10-04 19:07:24 +08:00
}
if (!data['no-emoji']) {
2019-08-15 01:42:50 +08:00
replaceColons($content.window.document.body, emojis)
replaceColons($excerpt.window.document.body, emojis)
2018-10-04 19:07:24 +08:00
}
2019-08-15 01:42:50 +08:00
data.content = $content.window.document.body.innerHTML
data.excerpt = $excerpt.window.document.body.innerHTML
2018-10-04 19:07:24 +08:00
return data
})
hexo.extend.helper.register('github_emoji', name => renderEmoji(emojis, name))
hexo.extend.tag.register('github_emoji', args => renderEmoji(emojis, args[0]))
}
2019-08-15 01:42:50 +08:00
function replaceColons (node, emojis) {
if (!node || !node.childNodes) { return }
for (let i = node.childNodes.length - 1; i >= 0; i--) {
const child = node.childNodes[i]
if (child.tagName === 'PRE' || child.tagName === 'CODE') { return }
if (child.nodeType === 3) {
2018-10-04 19:07:24 +08:00
const content = child.data.replace(
/:(\w+):/ig,
(match, p1) => emojis[p1] ? renderEmoji(emojis, p1) : match,
)
if (content !== child.data) {
2019-08-15 01:42:50 +08:00
child.replaceWith(JSDOM.fragment(content))
2018-10-04 19:07:24 +08:00
}
2019-08-15 01:42:50 +08:00
} else {
replaceColons(child, emojis)
2018-10-04 19:07:24 +08:00
}
2019-08-15 01:42:50 +08:00
}
2018-10-04 19:07:24 +08:00
}
function loadCustomEmojis (customEmojis) {
2016-12-23 18:46:01 +08:00
// JSON string
2018-10-04 19:07:24 +08:00
if (_.isString(customEmojis)) {
2016-12-22 19:11:28 +08:00
try {
2018-10-04 19:07:24 +08:00
customEmojis = JSON.parse(customEmojis)
Object.keys(customEmojis).forEach(name => {
if (_.isString(customEmojis[name])) {
customEmojis[name] = {
src: customEmojis[name],
2016-12-23 18:46:01 +08:00
}
}
})
2018-10-04 19:07:24 +08:00
} catch (err) {
customEmojis = {}
console.warn('hexo-filter-github-emojis: Custom emojis not valid. Skipped.')
2016-12-22 19:11:28 +08:00
}
}
2018-10-04 19:07:24 +08:00
if (!_.isObject(customEmojis)) {
customEmojis = {}
}
2016-12-22 19:11:28 +08:00
2018-10-04 19:07:24 +08:00
Object.values(customEmojis).forEach(emoji => {
if (emoji.codepoints && !_.isArray(emoji.codepoints)) {
emoji.codepoints = emoji.codepoints.split(' ')
2016-12-22 19:11:28 +08:00
}
})
2018-10-04 19:07:24 +08:00
}
2016-12-22 19:11:28 +08:00
2018-10-04 19:07:24 +08:00
function renderEmoji (emojis, name) {
if (!emojis[name]) { return name }
2018-10-04 19:07:24 +08:00
const styles = _.isObject(options.styles)
? Object.keys(options.styles)
.filter(k => _.isString(options.styles[k]))
.map(k => k + ':' + options.styles[k])
: []
2018-10-04 19:07:24 +08:00
if (options.inject) {
styles.push(
'color: transparent',
`background:no-repeat url(${emojis[name].src}) center/contain`,
)
} else {
styles.push(`background-image:url(${emojis[name].src})`)
}
2017-06-26 15:52:12 +08:00
2018-10-04 19:07:24 +08:00
const codepoints = emojis[name].codepoints
? emojis[name].codepoints.map(c => `&#x${c};`).join('')
: ' '
2017-06-26 15:51:51 +08:00
2018-10-04 19:07:24 +08:00
return `<span class="${options.className}" style="${styles.join(';')}" data-src="${emojis[name].src}">${codepoints}</span>`
2016-12-22 19:11:28 +08:00
}