summaryrefslogtreecommitdiff
path: root/assets/js/pjax.js
blob: 324087ab88d156961be376ad4beda23a4df3c932 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
 * PJAX 初始化与页面切换重绑定脚本
 * 依赖:jQuery, jquery.pjax.min.js
 * 加载顺序:在 jquery.pjax.min.js 之后,body 末尾
 */

(function ($) {
    // ========== 常量 ==========
    var CONTAINER = '#pjax-container';
    var PJAX_OPTS = {
        container: CONTAINER,
        fragment: CONTAINER,
        timeout: 8000,
        scrollTo: false
    };

    // ========== 各组件重初始化 ==========

    /** AI 摘要(post.html 内联脚本,pjax 后由 executeScripts 触发) */
    function reinitAISummary() {
        if (typeof ai_gen === 'function' && $('#ai-output').length) {
            try { ai_gen(); } catch (e) { /* ignore */ }
        }
    }

    /** 关键词高亮 */
    function reinitHighlight() {
        var keyword = new URLSearchParams(window.location.search).get('kw');
        if (!keyword) return;
        keyword = keyword.trim();
        if (!keyword) return;

        var escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        var regex = new RegExp('(' + escaped + ')', 'gi');
        var escapeHTML = function (str) {
            return str.replace(/[&<>"']/g, function (t) {
                return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[t] || t;
            });
        };
        function walk(node) {
            $(node).contents().each(function () {
                if (this.nodeType === Node.TEXT_NODE) {
                    var $t = $(this);
                    var text = escapeHTML($t.text());
                    if (regex.test(text)) $t.replaceWith(text.replace(regex, '<mark>$1</mark>'));
                } else if (this.nodeType === Node.ELEMENT_NODE && !$(this).is('script, style, noscript, textarea')) {
                    walk(this);
                }
            });
        }
        $('section').each(function () { walk(this); });
    }

    /** Google Analytics 页面浏览事件 */
    function trackPageView() {
        if (typeof gtag === 'function') {
            gtag('config', window._gaId || '', { page_path: window.location.pathname });
        }
    }

    /** Live2D 重初始化 */
    var _live2dSelectors = ['.post-link', '#search-input'];
    var _live2dDelegateBound = false;

    function reinitLive2d() {
        if (!window._live2d) return;
        var pathname = window.location.pathname;

        // 更新"想问这篇文章"相关状态(仅真正的文章页显示)
        $('#post_id').val(pathname);
        if ($(CONTAINER + ' #gitalk-container').length > 0) {
            $('.live_talk_input_name_body').show();
        } else {
            $('.live_talk_input_name_body').hide();
            $('#load_this').prop('checked', false);
        }

        // 音乐按钮:根据当前页面是否有 BGM 输入来显示/隐藏
        if (typeof window._live2d.initBGM === 'function') {
            window._live2d.initBGM();
        }

        // 事件委托绑定(只执行一次)
        if (!_live2dDelegateBound && typeof String.prototype.renderTip === 'function') {
            var selector = CONTAINER + ' ' + _live2dSelectors.join(', ' + CONTAINER + ' ');
            $(document).on('mouseover._live2d_pjax', selector, function (e) {
                var $el = $(e.currentTarget || e.target);
                if ($el.is('.post-link')) {
                    window._live2d.showMessage('要看看 ' + $el.text() + ' 么?', 3000);
                } else if ($el.is('#search-input')) {
                    window._live2d.showMessage('在找什么东西呢,需要帮忙吗?', 3000);
                }
            });
            $(document).on('mouseout._live2d_pjax', selector, function () {
                if (window._live2d.showHitokoto) window._live2d.showHitokoto();
            });
            _live2dDelegateBound = true;
        }

        // 欢迎语
        if (typeof window._live2d.showMessage === 'function') {
            window._live2d.showMessage(getWelcomeText(pathname), 6000);
        }
    }

    // ========== PJAX 导航 ==========

    /** PJAX 完成后的统一处理 */
    function doPjaxComplete() {
        $('body').removeClass('pjax-loading');
        // 清理可能残留的浮层(如推荐文章 tooltip,hover 后点击跳转时 mouseleave 来不及触发)
        $('.content-tooltip').remove();
        onPjaxComplete();
    }

    /** 暴露给模板内 onclick/onchange 调用的导航函数 */
    window.go = function (url) {
        $.pjax({ url: url, ...PJAX_OPTS });
    };

    // ========== 初始化 ==========

    /** 每次 pjax 完成后执行所有重初始化 */
    function onPjaxComplete() {
        initVisitors();
        initCopyButtons();
        reinitHighlight();
        reinitAISummary();
        reinitLive2d();
        trackPageView();
        window.scrollTo(0, 0);
    }

    $(document).ready(function () {
        // 排除列表:外链、锚点、静态资源、Live2D 目录
        var exclude = ':not([target="_blank"]):not([href^="http"]):not([href^="//"])' +
            ':not([href^="mailto"]):not([href^="#"])' +
            ':not([href$=".xml"]):not([href$=".json"]):not([href$=".tgz"]):not([href$=".zip"])' +
            ':not([href^="/Live2dHistoire"])';
        $(document).pjax('a' + exclude, PJAX_OPTS.container, PJAX_OPTS);
        $(document).on('submit', 'form#search-input-all', function (e) {
            $.pjax.submit(e, PJAX_OPTS.container, PJAX_OPTS);
        });
        $(document).on('pjax:send', function () {
            $('body').addClass('pjax-loading');
        });
        $(document).on('pjax:complete', doPjaxComplete);
        $(document).on('pjax:error', function (xhr, textStatus, error) {
            console.warn('[pjax] error, fallback:', error);
        });
        $(document).on('pjax:end', function (event, xhr, options) {
            var $container = $(options.container || PJAX_OPTS.container);

            $container.find('script[type="module"]').each(function () {
                var oldScript = this;
                var newScript = document.createElement('script');
                newScript.type = 'module';

                // 如果是外链脚本 (<script src="..."></script>)
                if (oldScript.src) {
                    newScript.src = oldScript.src;
                } else {
                    // 如果是行内脚本 (<script>...code...</script>)
                    newScript.textContent = oldScript.textContent;
                }
                // 插入到 body 中触发浏览器执行
                document.body.appendChild(newScript);

                // 运行完后建议移除,防止 DOM 变得混乱(不影响模块执行)
                newScript.remove();
            });
        });
    });

})(jQuery);