Arn0's Echo

Halo主题定制——寒山启用DisqusJS

2020-04-12 · 19 min read
Halo

寒山是我迁移到Halo后用的第二款主题(第一款当然就是Halo自带的Anatole啦),也是我目前为止最满意的一款主题。只是美中不足的是这款主题不支持DisqusJS,有点不习惯,于是乎就搞上了这玩意。

这里说的主题是寒山的v1.4.3

项目主页在这里

首先为评论插件创建dsqjs对象

module/comment.ftl原本的<script></script>标签之间的内容全部注释或者删掉,然后创建loadDisqusJS()函数,并通过setTimeout延时调用loadDisqusJS()函数。

修改后如下

<#macro comment target,type>
	<#if !post.disallowComment!false>
	<!-- DisqusJS jsDelivr -->
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/disqusjs@1.2/dist/disqusjs.css">
	<script src="https://cdn.jsdelivr.net/npm/disqusjs@1.2/dist/disqus.js"></script>
		<script>
		function loadDisqusJS() {
			var dsqjs = new DisqusJS({
				shortname: '',
				siteName: '',
				identifier: document.location.origin + document.location.pathname + document.location.search,
				url: document.location.origin + document.location.pathname + document.location.search,
				title: document.title,
				api: '',
				apikey: '',
				admin: '',
				adminLabel: ''
				});
			}
        	setTimeout(function(){ loadDisqusJS(); }, 2000);
		</script>
      	<div id="disqus_thread" style="margin: 30px;"></div>
	</#if>
</#macro>

其中disqus_thread容器的位置决定了Disqus/DisqusJS出现的位置。

至于margin嘛,可以换其他数值,数值越大,评论框旁边的空隙就越大,评论框自然就越小,可以通过这种办法控制评论框大小。

这上面的代码还引入了DisqusJS的资源,不过这些没啥介绍的,我就不多说了。

下面是需要配置的内容

这个我是直接在DisqusJS项目主页剽窃的(小声)

shortname {string}

siteName {string}

identifier {string}

  • 当前页面的 identifier,用来区分不同页面
  • 建议,默认值为 document.location.origin + document.location.pathname + document.location.search

url {string}

  • 当前页面的 URL,Disqus 的爬虫会爬取该 URL 获取页面相关信息
  • 建议,默认值为 document.location.origin + document.location.pathname + document.location.search

title {string}

  • 当前页面的标题,如果没有设置默认为当前页面的标题。当页面标题中有其他信息(比如站点名称)而不想在 Disqus 中展示时,可以设置此项。
  • 非必须,默认值为 document.title

api {string}

  • DisqusJS 请求的 API Endpoint,通常情况下你应该配置一个 Disqus API 的反代并填入反代的地址。你也可以直接使用 DISQUS 官方 API 的 Endpoint https://disqus.com/api/,或是使用我搭建的 Disqus API 反代 Endpoint https://disqus.skk.moe/disqus/。如有必要可以阅读关于搭建反代的 相关内容
  • 建议,默认值为 https://disqus.skk.moe/disqus/

apikey {string || Array}

  • DisqusJS 向 API 发起请求时使用的 API Key,你应该在配置 Disqus Application 时获取了 API Key
  • DisqusJS 支持填入一个 包含多个 API Key 的 Array,在每次请求时会随机使用其中一个;如果你只填入一个 API Key,可以填入 string 或 Array。
  • 必填,无默认值

以下配置和 Disqus Moderator Badge 相关,缺少一个都不会显示 Badge

admin {string}

adminLabel {string}


你看,到这里基本上是可以使用了。
Disqus_Done

Disqus_Done

但如果你开启了全站PJAX,你会惊奇的发现,评论框在切换页面时并不会刷新。怎么办呢?难道只能关掉PJAX了吗?

PJAX支持

我们可以在module/script.ftl增加刷新的内容
找到// 重新加载 评论那一部分代码,将以下内容全部注释或删掉

// 重新加载 评论
$('script[data-pjax-comment]').each(function () {
	$(this).parent().append($(this).remove());
});

if ($("#page").find('.post-page').length > 0) {
	window.removeEventListener('scroll', post.tocScroll, false);

上面这些代码原本是为原生评论系统服务的,目的就是为了在PJAX刷新时可以自动重载评论框。

当然我们使用DisqusJS的话那就用不着上面这些代码了,直接调用一个函数就够了

loadDisqusJS();

修改后的module/script.ftl如下

<#include "mermaid.ftl">
<script src="//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
<script src="${theme_base!}/assets/media/scripts/plugins.min.js?ver=${.now?long}"></script>
<script src="${theme_base!}/assets/media/scripts/main.min.js?ver=${.now?long}"></script>
<script src="//cdn.jsdelivr.net/npm/velocity-animate@1.5.2/velocity.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/velocity-animate@1.5.2/velocity.ui.min.js"></script>
<#if settings.auto_night_mode>
    <script src="//cdn.jsdelivr.net/gh/hshanx/halo-comment-normal@v1.0.0/dist/halo-comment.min.js"></script>
<#else>
    <script src="${options.comment_internal_plugin_js!'//cdn.jsdelivr.net/gh/hshanx/halo-comment-normal@v1.0.0/dist/halo-comment.min.js'}"></script>
</#if>

<#if settings.Aplayer?? && settings.Aplayer != ''>
    <script src="//cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js"></script>
    <script src="//cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>
<#else>
    <script type="text/javascript">
        // Smooth scroll to anchors
        var scroll = new SmoothScroll('[data-scroll]', {
            speed: 300,
            updateURL: false,
        })
    </script>
</#if>

<#-- 暗夜模式 -->
<#if settings.auto_night_mode!true>
    <script type="text/javascript">
        var nightModeStartTime = ${settings.night_mode_start_time?default('18')};
        var nightModeEndTime = ${settings.night_mode_end_time?default('6')};
    </script>
    <script src="${theme_base!}/assets/media/scripts/night-mode.min.js?ver=${.now?long}"></script>
</#if>

<#if settings.visit_statistics!false>
    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
</#if>

<#-- katex-->
<#if settings.enabled_mathjax!true>
    <script defer src="//cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"></script>
    <script defer src="//cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js"
            onload="if (document.getElementById('post-content') ) {renderMathInElement(document.getElementById('post-content'),katex_config)}"></script>
</#if>

<#-- gallery  -->
<#--<script src="//cdn.jsdelivr.net/npm/lightgallery@1.6.8/dist/js/lightgallery.min.js"></script>-->
<script src="//cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>
<!--图片预览插件-->
<script data-pjax-viewer src="//cdn.jsdelivr.net/npm/viewerjs@1.5.0/dist/viewer.min.js"></script>
<script data-gallery src="${theme_base!}/assets/media/scripts/gallery.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/highlight.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/highlightjs-line-numbers.js@2.7.0/dist/highlightjs-line-numbers.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/social-share.js@1.0.16/dist/js/social-share.min.js"></script>


<div class="qr-code-wrap" role="dialog">
    <div role="document" class="qr-code" style="transform-origin: 201px 294px;">
        <span class="closinglayer"><svg viewBox="64 64 896 896" focusable="false" class="" data-icon="close" width="1em"
                                        height="1em" fill="currentColor" aria-hidden="true"><path
                        d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg>
        </span>
        <div style="text-align: center;padding: 10px 0;">
            <#if settings.QR_code_zfb??>
                <img class="qr_code_zfb" src="${settings.QR_code_zfb!}"/>
            </#if>
            <#if settings.QR_code_wx??>
                <img class="qr_code_wx" src="${settings.QR_code_wx!}"/>
            </#if>
        </div>
        <#if settings.QR_code_zfb?? && settings.QR_code_wx??>
            <div class="switch-btn">
                <span class="zfb-btn">支付宝</span>
                <span class="wx-btn">微信</span>
            </div>
        </#if>
    </div>
</div>

<#--目录-->
<#if settings.post_toc!true>
    <script src="//cdn.jsdelivr.net/npm/tocbot@4.4.2/dist/tocbot.min.js"></script>
</#if>

<script type="application/javascript">
    var displayReadProgress = <#if (settings.open_read_progress)??>${settings.open_read_progress?c}<#else>true</#if>;
</script>
<script src="${theme_base!}/assets/media/scripts/post.min.js?ver=${.now?long}"></script>
<style>
    /* 阅读进度的进度条颜色 */
    #readProgress .read-progress-bar {
        background: ${settings.progress_color?default('#2474b5')} !important;
        height: 0.1875rem;
    }
</style>


<#if settings.TimeStatistics??>
    <script type="text/javascript">
        // 建站时间统计
        function show_date_time() {
            if ($("#span_dt_dt").length > 0) {
                window.setTimeout("show_date_time()", 1000);
                BirthDay = new Date("${settings.TimeStatistics!}");
                today = new Date();
                timeold = (today.getTime() - BirthDay.getTime());
                sectimeold = timeold / 1000;
                secondsold = Math.floor(sectimeold);
                msPerDay = 24 * 60 * 60 * 1000;
                e_daysold = timeold / msPerDay;
                daysold = Math.floor(e_daysold);
                e_hrsold = (e_daysold - daysold) * 24;
                hrsold = Math.floor(e_hrsold);
                e_minsold = (e_hrsold - hrsold) * 60;
                minsold = Math.floor((e_hrsold - hrsold) * 60);
                seconds = Math.floor((e_minsold - minsold) * 60);
                span_dt_dt.innerHTML = daysold + "天" + hrsold + "小时" + minsold + "分" + seconds + "秒";
            }
        }

        show_date_time();
    </script>
</#if>

<#if settings.Custom_js_foot??>
    <script type="text/javascript">
        ${settings.Custom_js_foot!}
    </script>
</#if>

<#if settings.Custom_js_foot_src??>
    ${settings.Custom_js_foot_src!}
</#if>

<#if settings.pjax_enabled!false>
    <script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js"></script>
    <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.css">

    <script>
        var socialDisabled = '${settings.share_disabeld?default('')}';
        var pjax = new Pjax({
            elements: 'a[href]:not([href^="#"]):not([data-not-pjax]), form', // default is "a[href], form[action]"
            cacheBust: false,
            debug: false,
            selectors: [
                'title',
                '#page'
            ]
        });

        //在Pjax请求开始后触发
        document.addEventListener('pjax:send', function () {
            NProgress.start();
        });

        //在Pjax请求完成后触发
        document.addEventListener('pjax:complete', function (e) {
            NProgress.done();

            // 加载相册
            if ($("#page").find('.photos-page').length > 0) {
                photo.loadGallery();
                // $('script[data-pjax-viewer]').each(function () {
                //     $(this).remove()
                // });
            }

            han.initLazyLoad();
            // 整个页面延迟加载
            han.lazyLoad();

            // card 延迟加载
            han.lazyLoadCardItem()

            //重载
            if (typeof _hmt !== 'undefined') {
                // support 百度统计
                _hmt.push(['_trackPageview', location.pathname + location.search]);
            }
            if (typeof ga !== 'undefined') {
                // support google analytics
                ga('send', 'pageview', location.pathname + location.search);
            }

            // 菜单高亮
            han.highlightMenu();

            // 小屏幕菜单隐藏
            han.makeMenuInvisible();

            //  关闭搜索框
            $(".search-popup").velocity("transition.expandOut", { duration: 300 });

            // 重新加载 评论
          	loadDisqusJS();

                // 赞赏
                post.appreciate();

                // 初始化toc
                post.initToc()

                // 删除文章第一个 <ul>
                post.removeFirstUL()

                // 目录事件
                post.scrollTocFixed();

                // 搞一个阅读进度,为了提高准确度,数据都要实时获取
                post.readProgress();

                // 代码块
                post.loadHighlight();

                // 按钮事件
                post.appreciateModel()

                // 分享
                post.toggleSocialShare()

                // 图片预览
                post.initViewer()

                // 目录悬浮时间
                post.tocHover();


                try {
                    post.shareIcon()

                    if (renderMathInElement && typeof renderMathInElement !== 'undefined') {
                        renderMathInElement(document.getElementById('post-content'), katex_config);
                    }

                    if (mermaid && typeof mermaid !== 'undefined') {
                        mermaid.initialize();
                    }
                } catch (e) {
                    console.log("error");
                }
                // 刷新
                han.refreshLazyLoad();
            } else {
                han.initLazyLoad()
            }


        });

        document.addEventListener('pjax:end', function () {

        });

        //Pjax请求失败后触发,请求对象将作为一起传递event.options.request
        document.addEventListener('pjax:error', function () {
            NProgress.done();
            bar('系统出现问题,请手动刷新一次', '3000');
        });
    </script>
</#if>


<script type="text/javascript">
    //console.clear();
    console.log("%c 有朋自远方来, 不亦说乎.", "background:#24272A; color:#ffffff", "");
    console.log("%c Github %c", "background:#24272A; color:#ffffff", "", "https://github.com/hshanx");
    console.log("%c 版本号: %c", "background:#24272A; color:#ffffff", "", "1.4.2");
</script>


这样,PJAX只需通过重新加载loadDisqusJS()函数,就可以在加载新页面时刷新评论框。

当然,我上面说的“重新加载”,并不是真正意义上的重载。JavaScript并没有真正意义上的重载函数。我上面的操作主要是用loadDisqusJS()来覆盖loadDisqusJS(),即后面的会覆盖前面的函数。老实说其实还有其他更优雅的方法。但是对于这种需求,上面的方案就足够了,为了避免跑题这里就不展开说了。

嘛,还有个小Bug

DisqusJS_Night
我只是演示而已,蝉時雨大佬应该不会介意吧(害怕.webp)。

标签的文字和背景颜色融在一起,导致评论里啥都看不清了,这很显然是不行的。解决办法也很简单,在切换模式后重载Disqus就好,我们接下来就解决这个问题。

修改night-mode.js

assets/scripts/night-mode.js里面将nightMode.click函数里面这些代码注释或者删掉,这些代码是为Halo原生评论系统服务的,目的是为了在切换模式时重载评论框避免上面的情况发生。

if (typeof renderComment === 'function') {
renderComment();
}

基于上面的代码逻辑,我们用DisqusJS的话,可以做得更简单,只需换成

// 重新加载DisqusJS,延时500ms
setTimeout(function(){ loadDisqusJS(); }, 500);

即可。

修改后的night-mode.js如下

var nightModeId = 'nightMode';

var darkMode = {
    autoNightMode: function () {

        var nightModes = $('.night-mode');
        var day = new Date();
        var D = day.getHours();
        var isNightMode = hanUtils.getLocalStorage(nightModeId);
        if (D <= nightModeStartTime && D > nightModeEndTime) {
            // 白天
            if (isNightMode === true) {
                // 是暗黑模式
                darkMode.changeNightMode(nightModes);
                return;
            }
            darkMode.changeLightMode(nightModes);
        } else {
            // 晚上
            if (isNightMode === false) {
                // 不是暗黑模式
                darkMode.changeLightMode(nightModes);
                return;
            }
            darkMode.changeNightMode(nightModes);
        }
        if (typeof renderComment === 'function') {
            renderComment();
        }
    },

    changeLightMode: function (nightModes) {
        $(document.body).removeClass('night');
        for (var i = 0; i < nightModes.length; i++) {
            var nightMode = $(nightModes[i]);
            nightMode.addClass('fa-moon-o');
            nightMode.removeClass('fa-lightbulb-o');
        }
        hanUtils.setLocalStorage(nightModeId, false)
    },

    changeNightMode: function (nightModes) {
        $(document.body).addClass('night');
        for (var i = 0; i < nightModes.length; i++) {
            var nightMode = $(nightModes[i]);
            nightMode.addClass('fa-lightbulb-o');
            nightMode.removeClass('fa-moon-o');
        }
        hanUtils.setLocalStorage(nightModeId, true)
    },

    nightModeFuc: function () {
        var nightModes = $('.night-mode');
        if (!nightModes) {
            return;
        }
        for (var i = 0; i < nightModes.length; i++) {
            var nightMode = $(nightModes[i]);
            darkMode.doFuncNightMode(nightMode);
        }

    },

    doFuncNightMode: function (nightMode) {
        var nightModeBtn = $('.night-mode');
        if ($(document.body).hasClass('night')) {
            nightModeBtn.addClass('fa-lightbulb-o');
            nightModeBtn.removeClass('fa-moon-o');
        } else {
            nightModeBtn.addClass('fa-moon-o');
            nightModeBtn.removeClass('fa-lightbulb-o');
        }


        nightMode.click(function (e) {
            if (nightMode.hasClass('fa-moon-o')) {
                $(document.body).addClass('night');
                nightModeBtn.addClass('fa-lightbulb-o');
                nightModeBtn.removeClass('fa-moon-o');
                hanUtils.setLocalStorage(nightModeId, true);

            } else if (nightMode.hasClass('fa-lightbulb-o')) {
                $(document.body).removeClass('night');
                nightModeBtn.addClass('fa-moon-o');
                nightModeBtn.removeClass('fa-lightbulb-o');

                hanUtils.setLocalStorage(nightModeId, false);
            }
            $(document.body).removeClass('sidebar-opened');
                                              
			// 重新加载DisqusJS
			setTimeout(function(){ loadDisqusJS(); }, 500);
        })
    }
}

$(function () {
// 自动暗黑模式
    darkMode.autoNightMode();

// 暗黑模式
    darkMode.nightModeFuc();

})

修改好后记得压缩代码并覆盖night-mode.min.js里面的内容,不然是不会生效的。因为页面引入的是night-mode.min.js,而不是night-mode.jsnight-mode.js只是编辑用的而已。

保存后刷新就好了

DisqusJS_Night_Done
也是演示

为什么是500毫秒?

主要是为了在页面变色完成之前避免遇到DisqusJS抢先加载的情况,当然这里的时间可以调得更短,或者用事件检测器也行。还有开头module/comment.ftl那里的setTimeout(function(){ loadDisqusJS(); }, 2000);也是为了应对这种情况。

关于DisqusJS的夜间模式

又是一个大坑,过几天等我有时间再填吧(咕)。
DisqusJS_unDone

现在DisqusJS的功能是齐全的,包括Disqus的夜间模式。但美中不足的就是DisqusJS的夜间模式还是不行,目前还没有解决办法。暂时只能先禁用夜间模式。