tona-plugins 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +48 -0
  2. package/index.d.ts +32 -0
  3. package/package.json +51 -0
  4. package/src/constants/cdn.js +12 -0
  5. package/src/index.js +30 -0
  6. package/src/plugins/background/index.js +50 -0
  7. package/src/plugins/background/index.scss +0 -0
  8. package/src/plugins/barrage/index.js +104 -0
  9. package/src/plugins/barrage/index.scss +16 -0
  10. package/src/plugins/catalog/index.js +212 -0
  11. package/src/plugins/catalog/index.scss +0 -0
  12. package/src/plugins/charts/index.js +60 -0
  13. package/src/plugins/charts/index.scss +16 -0
  14. package/src/plugins/clickEffects/index.js +137 -0
  15. package/src/plugins/clickEffects/index.scss +0 -0
  16. package/src/plugins/codeCopy/index.js +94 -0
  17. package/src/plugins/codeCopy/index.scss +65 -0
  18. package/src/plugins/codeHighlight/index.js +35 -0
  19. package/src/plugins/codeHighlight/index.scss +216 -0
  20. package/src/plugins/codeHighlight/themes.js +60 -0
  21. package/src/plugins/codeLang/index.js +51 -0
  22. package/src/plugins/codeLang/index.scss +28 -0
  23. package/src/plugins/codeLinenumbers/index.js +33 -0
  24. package/src/plugins/codeLinenumbers/index.scss +61 -0
  25. package/src/plugins/codeTrafficLight/index.js +18 -0
  26. package/src/plugins/codeTrafficLight/index.scss +0 -0
  27. package/src/plugins/colorMode/index.js +81 -0
  28. package/src/plugins/colorMode/index.scss +0 -0
  29. package/src/plugins/commentsAvatars/index.js +85 -0
  30. package/src/plugins/commentsAvatars/index.scss +3 -0
  31. package/src/plugins/darkMode/index.js +115 -0
  32. package/src/plugins/darkMode/index.scss +67 -0
  33. package/src/plugins/donation/index.js +45 -0
  34. package/src/plugins/donation/index.scss +63 -0
  35. package/src/plugins/emoji/index.js +167 -0
  36. package/src/plugins/emoji/index.scss +155 -0
  37. package/src/plugins/footer/index.js +35 -0
  38. package/src/plugins/footer/index.scss +54 -0
  39. package/src/plugins/imagePreview/index.js +98 -0
  40. package/src/plugins/imagePreview/index.scss +51 -0
  41. package/src/plugins/license/index.js +62 -0
  42. package/src/plugins/license/index.scss +51 -0
  43. package/src/plugins/live2d/index.js +67 -0
  44. package/src/plugins/live2d/index.scss +0 -0
  45. package/src/plugins/live2d/live2d-models.js +21 -0
  46. package/src/plugins/lock/index.js +77 -0
  47. package/src/plugins/lock/index.scss +56 -0
  48. package/src/plugins/musicPlayer/index.js +81 -0
  49. package/src/plugins/musicPlayer/index.scss +68 -0
  50. package/src/plugins/notation/index.js +73 -0
  51. package/src/plugins/notation/index.scss +12 -0
  52. package/src/plugins/notice/index.js +17 -0
  53. package/src/plugins/notice/index.scss +46 -0
  54. package/src/plugins/postBottomImage/index.js +27 -0
  55. package/src/plugins/postBottomImage/index.scss +26 -0
  56. package/src/plugins/postMessage/index.js +89 -0
  57. package/src/plugins/postMessage/index.scss +81 -0
  58. package/src/plugins/postTopImage/index.js +28 -0
  59. package/src/plugins/postTopImage/index.scss +29 -0
  60. package/src/plugins/qrcode/index.js +42 -0
  61. package/src/plugins/qrcode/index.scss +10 -0
  62. package/src/plugins/signature/index.js +47 -0
  63. package/src/plugins/signature/index.scss +18 -0
  64. package/src/plugins/toast/index.js +49 -0
  65. package/src/plugins/tools/index.js +251 -0
  66. package/src/plugins/tools/index.scss +114 -0
  67. package/src/plugins/webTag/index.js +40 -0
  68. package/src/plugins/webTag/index.scss +0 -0
  69. package/src/utils/cnblog.js +282 -0
  70. package/src/utils/helpers.js +418 -0
  71. package/src/utils/shared.js +2 -0
@@ -0,0 +1,67 @@
1
+ import { getLive2dOptions } from 'tona-options'
2
+ import { getCurrentPage } from '../../utils/cnblog'
3
+ import { loadScript, randomProperty, userAgent } from '../../utils/helpers'
4
+ import { live2dModels } from './live2d-models'
5
+
6
+ const live2dBase = 'https://cdn.jsdelivr.net/gh/guangzan/awesCnb-live2dModels'
7
+ const live2djs =
8
+ 'https://files.cnblogs.com/files/guangzan/live2d.min.js?t=1688786567&download=true'
9
+
10
+ /**
11
+ * 构建模型容器
12
+ * @param {string} position
13
+ * @param {string} width
14
+ * @param {string} height
15
+ */
16
+ function buildContainer(position, width, height) {
17
+ const ele = `<canvas id="model" style="position:fixed;${position}:0;bottom:0;z-index:30;pointer-events: none;" width="${width}"height="${height}" ></canvas>`
18
+ $('body').append(ele)
19
+ }
20
+
21
+ /**
22
+ * 设置间距
23
+ * @param {string} position
24
+ * @param {string} gap
25
+ */
26
+ function setGap(position, gap) {
27
+ if (gap === 'default') {
28
+ return
29
+ }
30
+ $('#model').css(position, gap)
31
+ }
32
+
33
+ /**
34
+ * 加载模型
35
+ * @param {string} model
36
+ */
37
+ function loadModel(model) {
38
+ const live2dModel =
39
+ model === 'random'
40
+ ? live2dModels[randomProperty(live2dModels)]
41
+ : live2dModels[model]
42
+
43
+ const url = `${live2dBase}@latest/${live2dModel}`
44
+ loadScript(live2djs, () => {
45
+ // eslint-disable-next-line no-undef
46
+ loadlive2d('model', url)
47
+ })
48
+ }
49
+
50
+ export function live2d(_, devOptions) {
51
+ const { enable, page, agent, model, position, gap, width, height } =
52
+ getLive2dOptions(devOptions)
53
+
54
+ if (!enable) {
55
+ return
56
+ }
57
+ if (page !== getCurrentPage() && page !== 'all') {
58
+ return
59
+ }
60
+ if (agent !== userAgent() && agent !== 'all') {
61
+ return
62
+ }
63
+
64
+ buildContainer(position, width, height)
65
+ setGap(position, gap)
66
+ loadModel(model)
67
+ }
File without changes
@@ -0,0 +1,21 @@
1
+ /**
2
+ * live2d widgets
3
+ */
4
+
5
+ export const live2dModels = {
6
+ chitose: 'live2d-widget-model-epsilon2_1/assets/Epsilon2.1.model.json',
7
+ epsilon2_1: 'live2d-widget-model-chitose/assets/chitose.model.json',
8
+ 'haru-01': 'live2d-widget-model-haru/01/assets/haru01.model.json',
9
+ 'haru-02': 'live2d-widget-model-haru/02/assets/haru02.model.json',
10
+ hijiki: 'live2d-widget-model-hijiki/assets/hijiki.model.json',
11
+ tororo: 'live2d-widget-model-tororo/assets/tororo.model.json',
12
+ ryoufuku: 'misaki/ryoufuku.model.json',
13
+ seifuku: 'misaki/seifuku.model.json',
14
+ shifuku: 'misaki/shifuku.model.json',
15
+ vert_classic: 'vert_classic/vert_classic.model.json',
16
+ vert_normal: 'vert_normal/vert_normal.model.json',
17
+ vert_swimwear: 'vert_swimwear/vert_swimwear.model.json',
18
+ 小埋: '小埋/13.json',
19
+ 伊芙加登: '伊芙加登/14.json',
20
+ 玉藻前: '玉藻前/16.json',
21
+ }
@@ -0,0 +1,77 @@
1
+ // 锁屏
2
+ import { getLockScreenOptions } from 'tona-options'
3
+ import { typedJs } from '../../constants/cdn'
4
+ import { loadScript } from '../../utils/helpers'
5
+
6
+ const randomImage = 'https://api.mz-moe.cn/img.php'
7
+
8
+ let typed
9
+
10
+ /**
11
+ * 创建元素
12
+ */
13
+ function build() {
14
+ $('body').append(`
15
+ <div class='lock-screen'>
16
+ <div class="lock-screen-weather"></div>
17
+ <div class="lock-screen-user">
18
+ <div class='lock-screen-text'>
19
+ <span></span>
20
+ </div>
21
+ </div>
22
+ <div class="lock-screen-close">🔑</div>
23
+ </div>`)
24
+ }
25
+
26
+ /**
27
+ * 设置锁屏背景
28
+ * @param {*} background
29
+ */
30
+ function setBackground(background) {
31
+ const image = background === '' ? randomImage : background
32
+ $('.lock-screen').css('background-image', `url(${image})`)
33
+ }
34
+
35
+ /**
36
+ * 打开锁屏
37
+ * @param {*} strings
38
+ */
39
+ function handleOpen(strings) {
40
+ const typedOpts = {
41
+ strings: strings.length ? strings : ['快去自定义你的个性签名吧~'],
42
+ typeSpeed: 100,
43
+ }
44
+ $('#header').dblclick(() => {
45
+ $('body').addClass('overflow')
46
+ $('.lock-screen').css('top', '0')
47
+ // eslint-disable-next-line no-undef
48
+ typed = new Typed('.lock-screen-text span', typedOpts)
49
+ })
50
+ }
51
+
52
+ /**
53
+ * 关闭锁屏
54
+ */
55
+ function handleClose() {
56
+ $(document).on('click', '.lock-screen-close', () => {
57
+ $('.lock-screen').css('top', '-100vh')
58
+ typed.destroy()
59
+ setTimeout(() => {
60
+ $('body').removeClass('overflow')
61
+ }, 400)
62
+ })
63
+ }
64
+
65
+ export function lock(_, devOptions) {
66
+ const { enable, background, strings } = getLockScreenOptions(devOptions)
67
+ if (!enable) {
68
+ return
69
+ }
70
+ build()
71
+ setBackground(background)
72
+
73
+ loadScript(typedJs, () => {
74
+ handleOpen(strings)
75
+ handleClose()
76
+ })
77
+ }
@@ -0,0 +1,56 @@
1
+ .lock-screen {
2
+ position: fixed;
3
+ top: -100vh;
4
+ width: 100vw;
5
+ height: 100vh;
6
+ z-index: 999;
7
+ overflow: hidden;
8
+ background-color: #fff;
9
+ transition: top 0.3s;
10
+ background-repeat: no-repeat;
11
+ background-size: cover;
12
+ }
13
+
14
+ .lock-screen-weather {
15
+ position: absolute;
16
+ top: 20px;
17
+ left: 20px;
18
+ }
19
+
20
+ .lock-screen-close {
21
+ position: absolute;
22
+ top: 20px;
23
+ right: 20px;
24
+ font-size: 24px;
25
+ cursor: pointer;
26
+ }
27
+
28
+ .lock-screen-user {
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ justify-content: center;
33
+ margin-top: 30vh;
34
+ }
35
+
36
+ .lock-screen-user img {
37
+ border-radius: 50%;
38
+ width: 80px;
39
+ height: 80px;
40
+ }
41
+
42
+ .lock-screen-text {
43
+ margin-top: 50px;
44
+ }
45
+
46
+ .lock-screen-text span {
47
+ font-size: 22px;
48
+ font-weight: 700;
49
+ color: #fff;
50
+ text-shadow: 1px 1px 5px #999;
51
+ }
52
+
53
+ .overflow {
54
+ overflow: hidden;
55
+ overflow-x: inherit;
56
+ }
@@ -0,0 +1,81 @@
1
+ // 音乐播放器
2
+ import { getMusicPlayerOptions } from 'tona-options'
3
+ import { APlayerCss, APlayerJs } from '../../constants/cdn'
4
+ import { getCurrentPage } from '../../utils/cnblog'
5
+ import { loadLink, loadScript, userAgent } from '../../utils/helpers'
6
+
7
+ /**
8
+ * 构建音乐播放器
9
+ * @param {*} autoplay
10
+ * @param {*} audio
11
+ * @param {*} volume
12
+ * @param {*} lrc
13
+ */
14
+ function buildPlayer(autoplay, audio, volume, lrc) {
15
+ $('body').append('<div id="player" class="aplayer music-APlayer"></div>')
16
+
17
+ // eslint-disable-next-line no-undef
18
+ const ap = new APlayer({
19
+ container: document.getElementById('player'),
20
+ fixed: true,
21
+ preload: 'auto',
22
+ autoplay,
23
+ volume,
24
+ lrcType: lrc.enable ? lrc.type : null,
25
+ audio,
26
+ })
27
+
28
+ window.onbeforeunload = () => {
29
+ const audioTime = ap.audio.currentTime
30
+ localStorage.audioTime = audioTime
31
+ }
32
+
33
+ window.onload = () => {
34
+ ap.seek(localStorage.audioTime ? Number(localStorage.audioTime) : 0)
35
+ }
36
+
37
+ if (autoplay) {
38
+ $('.aplayer-lrc').show()
39
+ }
40
+
41
+ if (localStorage.playerState === 'on') {
42
+ ap.play()
43
+ }
44
+
45
+ ap.on('play', () => {
46
+ localStorage.playerState = 'on'
47
+ if (lrc.enable) {
48
+ $('.aplayer-lrc').show()
49
+ }
50
+ })
51
+
52
+ ap.on('pause', () => {
53
+ localStorage.playerState = 'off'
54
+ if (lrc.enable) {
55
+ $('.aplayer-lrc').hide()
56
+ }
57
+ })
58
+
59
+ if (lrc.enable && lrc.color !== '') {
60
+ $('.aplayer-lrc').css('color', lrc.color)
61
+ }
62
+ }
63
+
64
+ export function musicPlayer(_, devOptions) {
65
+ const { enable, page, agent, autoplay, audio, volume, lrc } =
66
+ getMusicPlayerOptions(devOptions)
67
+
68
+ if (!enable) {
69
+ return
70
+ }
71
+ if (page !== getCurrentPage() && page !== 'all') {
72
+ return
73
+ }
74
+ if (agent !== userAgent() && agent !== 'all') {
75
+ return
76
+ }
77
+ loadLink(APlayerCss)
78
+ loadScript(APlayerJs, () => {
79
+ buildPlayer(autoplay, audio, volume, lrc)
80
+ })
81
+ }
@@ -0,0 +1,68 @@
1
+ @use 'sass:map';
2
+
3
+ $player: () !default;
4
+
5
+ $player: map.merge(
6
+ (
7
+ bodyBackground: #fff,
8
+ ),
9
+ $player
10
+ );
11
+
12
+ $playerBodyBackground: map.get($player, bodyBackground);
13
+
14
+ #player.aplayer {
15
+ background: none;
16
+ }
17
+
18
+ #player.aplayer .aplayer-list ol li {
19
+ background: $playerBodyBackground;
20
+ }
21
+
22
+ #player {
23
+ &:hover {
24
+ .aplayer-list {
25
+ transform: scaleX(1);
26
+ left: 0;
27
+ }
28
+ .aplayer-body {
29
+ left: 0;
30
+ }
31
+ }
32
+ .aplayer-list {
33
+ position: relative;
34
+ transform: scaleX(0.838);
35
+ left: -34px;
36
+ }
37
+ .aplayer-body {
38
+ left: -66px;
39
+ background-color: $playerBodyBackground;
40
+ // &:hover {
41
+ // left: 0;
42
+ // }
43
+ .aplayer-info {
44
+ border-top: none;
45
+ }
46
+ .aplayer-miniswitcher {
47
+ background-color: $playerBodyBackground;
48
+ border-left: 1px solid #999;
49
+ }
50
+ }
51
+ .aplayer-lrc {
52
+ display: none;
53
+ .aplayer-lrc-contents {
54
+ color: inherit;
55
+ p {
56
+ color: inherit;
57
+ font-size: 14px;
58
+ text-shadow: none;
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ @media screen and (max-width: 767px) {
65
+ #player {
66
+ display: none;
67
+ }
68
+ }
@@ -0,0 +1,73 @@
1
+ import { getNotationOptions } from 'tona-options'
2
+ import { notationJs } from '../../constants/cdn'
3
+ import { getCurrentPage } from '../../utils/cnblog'
4
+ import { loadScript } from '../../utils/helpers'
5
+
6
+ const annotateList = [
7
+ {
8
+ page: 'all',
9
+ selector: '#Header1_HeaderTitle',
10
+ config: {
11
+ type: 'underline',
12
+ color: '#2196F3',
13
+ },
14
+ },
15
+ ]
16
+
17
+ /**
18
+ * 构建 annotate group
19
+ * @param {*} annotate
20
+ * @param {*} customList
21
+ */
22
+ function buildGroup(annotate, customList) {
23
+ const group = []
24
+ for (const { selector, page, config } of customList) {
25
+ if (page === 'all' || getCurrentPage() === page) {
26
+ const element = document.querySelectorAll(selector)
27
+ if (!element.length) {
28
+ continue
29
+ }
30
+ if (element.length === 1) {
31
+ group.push(annotate(document.querySelector(selector), config))
32
+ }
33
+ if (element.length > 1) {
34
+ element.forEach((item) => {
35
+ group.push(annotate(item, config))
36
+ })
37
+ }
38
+ }
39
+ }
40
+ return group
41
+ }
42
+
43
+ /**
44
+ * 构建 notation
45
+ * @param {*} annotate
46
+ * @param {*} annotationGroup
47
+ * @param {*} customList
48
+ */
49
+ function buildNotation(annotate, annotationGroup, customList) {
50
+ setTimeout(() => {
51
+ const group = buildGroup(annotate, customList)
52
+ const ag = annotationGroup(group)
53
+ ag.show()
54
+ }, 2000)
55
+ }
56
+
57
+ export function notation(_, devOptions, customList = annotateList) {
58
+ if (getCurrentPage() !== 'post') {
59
+ return
60
+ }
61
+ const { enable } = getNotationOptions(devOptions)
62
+ if (!enable) {
63
+ return
64
+ }
65
+ if (!customList.length) {
66
+ return
67
+ }
68
+
69
+ loadScript(notationJs, () => {
70
+ const { annotate, annotationGroup } = window.RoughNotation
71
+ buildNotation(annotate, annotationGroup, customList)
72
+ })
73
+ }
@@ -0,0 +1,12 @@
1
+ mark {
2
+ background: none;
3
+ }
4
+
5
+ u,
6
+ s {
7
+ text-decoration: none;
8
+ }
9
+
10
+ // strong {
11
+ // font-weight: normal;
12
+ // }
@@ -0,0 +1,17 @@
1
+ import { getNoticeOptions } from 'tona-options'
2
+ import { toast } from '../toast'
3
+
4
+ function shoot(contents) {
5
+ const length = contents.length
6
+ for (let i = 0; i < length; i++) {
7
+ toast(contents[i], 'info')
8
+ }
9
+ }
10
+
11
+ export function notice(_, devOptions) {
12
+ const { enable, contents } = getNoticeOptions(devOptions)
13
+ if (!enable && contents.length) {
14
+ return
15
+ }
16
+ shoot(contents)
17
+ }
@@ -0,0 +1,46 @@
1
+ .message-wrap {
2
+ position: fixed;
3
+ top: 20px;
4
+ right: 20px;
5
+ z-index: 999;
6
+ }
7
+ .gradient-border {
8
+ position: relative;
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ width: 400px;
13
+ height: 200px;
14
+ color: white;
15
+ font-family: 'Raleway';
16
+ font-size: 2.5rem;
17
+ --borderWidth: 3px;
18
+ background: #1d1f20;
19
+
20
+ border-radius: var(--borderWidth);
21
+ }
22
+ .gradient-border:after {
23
+ content: '';
24
+ position: absolute;
25
+ top: calc(-1 * var(--borderWidth));
26
+ left: calc(-1 * var(--borderWidth));
27
+ height: calc(100% + var(--borderWidth) * 2);
28
+ width: calc(100% + var(--borderWidth) * 2);
29
+ background: linear-gradient(60deg, #f79533, #f37055, #ef4e7b, #a166ab, #5073b8, #1098ad, #07b39b, #6fba82);
30
+ border-radius: calc(2 * var(--borderWidth));
31
+ z-index: -1;
32
+ animation: animatedgradient 3s ease alternate infinite;
33
+ background-size: 300% 300%;
34
+ }
35
+
36
+ @keyframes animatedgradient {
37
+ 0% {
38
+ background-position: 0% 50%;
39
+ }
40
+ 50% {
41
+ background-position: 100% 50%;
42
+ }
43
+ 100% {
44
+ background-position: 0% 50%;
45
+ }
46
+ }
@@ -0,0 +1,27 @@
1
+ // 在随笔详情页尾部图片
2
+ import { getPostBottomImageOptions } from 'tona-options'
3
+ import { getCurrentPage } from '../../utils/cnblog'
4
+
5
+ export function postBottomImage(_, devOptions) {
6
+ const { enable, img, height } = getPostBottomImageOptions(devOptions)
7
+
8
+ if (!enable) {
9
+ return
10
+ }
11
+ if (getCurrentPage() !== 'post') {
12
+ return
13
+ }
14
+ if (img === '') {
15
+ return
16
+ }
17
+
18
+ const ele = '<div id="custom-post-bottomimage"></div>'
19
+ const style = {
20
+ 'background-image': `url(${img})`,
21
+ }
22
+ if (height !== '') {
23
+ style.height = height
24
+ }
25
+ $('#cnblogs_post_body').after(ele)
26
+ $('#custom-post-bottomimage').css(style)
27
+ }
@@ -0,0 +1,26 @@
1
+ @use 'sass:map';
2
+
3
+ $postBottomimage: () !default;
4
+ $postBottomimage: map.merge(
5
+ (
6
+ imageHeight: 200px,
7
+ ),
8
+ $postBottomimage
9
+ );
10
+
11
+ $bottomImageHeight: map.get($postBottomimage, imageHeight);
12
+
13
+ #custom-post-bottomimage {
14
+ width: 100%;
15
+ height: $bottomImageHeight;
16
+ background-size: cover;
17
+ background-repeat: no-repeat;
18
+ margin-bottom: 10px;
19
+ border-radius: 2px;
20
+ }
21
+
22
+ @media screen and (max-width: 767px) {
23
+ #custom-post-bottomimage {
24
+ height: 130px;
25
+ }
26
+ }
@@ -0,0 +1,89 @@
1
+ import { isPostDetailsPage } from '../../utils/cnblog'
2
+ import { poll } from '../../utils/helpers'
3
+
4
+ function getPostInfo() {
5
+ const date = $('#post-date').text()
6
+ const viewCount = $('#post_view_count').text()
7
+ const commentCount = $('#post_comment_count').text()
8
+ const diggCount = $('#digg_count').text()
9
+
10
+ return {
11
+ date,
12
+ viewCount,
13
+ commentCount,
14
+ diggCount,
15
+ }
16
+ }
17
+
18
+ function createContainer() {
19
+ return $('<div>').addClass('post-message')
20
+ }
21
+
22
+ function createCategoriesAndTags() {
23
+ const wrap = $('<div>').addClass('message-top')
24
+
25
+ const createCategories = () => {
26
+ const categories = $('<div>').addClass('message-categories').text('📂')
27
+ if (!$('#BlogPostCategory a').length) {
28
+ categories.append('<a>未分类</a>')
29
+ return categories
30
+ }
31
+ $('#BlogPostCategory a').each(function () {
32
+ categories.append($(this).get(0))
33
+ })
34
+ return categories
35
+ }
36
+
37
+ const createTags = () => {
38
+ const tags = $('<div>').addClass('message-tags').text('🔖')
39
+ if (!$('#EntryTag a').length) {
40
+ tags.append('<a>无标签</a>')
41
+ return tags
42
+ }
43
+ $('#EntryTag a').each(function () {
44
+ tags.append($(this).get(0))
45
+ })
46
+ return tags
47
+ }
48
+
49
+ const categories = createCategories()
50
+ const tags = createTags()
51
+
52
+ wrap.append(categories).append(tags)
53
+ return wrap
54
+ }
55
+
56
+ /**
57
+ * 博文统计
58
+ */
59
+ function createPostStatistics() {
60
+ const { date, viewCount, commentCount, diggCount } = getPostInfo()
61
+ const wrap = $('<div>').addClass('message-bottom')
62
+
63
+ wrap
64
+ .append($('<span>').text(date))
65
+ .append($('<span>').text(`阅读: ${viewCount}`))
66
+ .append($('<span>').text(`评论: ${commentCount}`))
67
+ .append($('<span>').text(`推荐: ${diggCount}`))
68
+
69
+ return wrap
70
+ }
71
+
72
+ function buildPostMessage() {
73
+ const container = createContainer()
74
+ const categoriesAndTags = createCategoriesAndTags()
75
+ const postStatistics = createPostStatistics()
76
+ container.append(categoriesAndTags).append(postStatistics)
77
+ $('.post').prepend(container)
78
+ }
79
+
80
+ export function postMessage() {
81
+ if (!isPostDetailsPage()) {
82
+ return
83
+ }
84
+
85
+ poll(
86
+ () => $('#EntryTag a').length && $('#BlogPostCategory a').length,
87
+ buildPostMessage,
88
+ )
89
+ }