Mayx's Home Page
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
7.1 KiB

  1. /**
  2. * PJAX 初始化与页面切换重绑定脚本
  3. * 依赖jQuery, jquery.pjax.min.js
  4. * 加载顺序 jquery.pjax.min.js 之后body 末尾
  5. */
  6. (function ($) {
  7. // ========== 常量 ==========
  8. var CONTAINER = '#pjax-container';
  9. var PJAX_OPTS = {
  10. container: CONTAINER,
  11. fragment: CONTAINER,
  12. timeout: 8000,
  13. scrollTo: false
  14. };
  15. // ========== 各组件重初始化 ==========
  16. /** 访问量统计 */
  17. function reinitVisitors() {
  18. if (typeof BlogAPI === 'undefined') return;
  19. var apiBase = BlogAPI;
  20. if ($('.visitors').length === 1) {
  21. var $visitor = $('.visitors:first');
  22. $.get(apiBase + '/count_click_add?id=' + $visitor.attr('id'), function (data) {
  23. $visitor.text(Number(data));
  24. });
  25. } else if ($('.visitors-index').length > 0) {
  26. $('.visitors-index').each(function () {
  27. var $elem = $(this);
  28. $.get(apiBase + '/count_click?id=' + $elem.attr('id'), function (data) {
  29. $elem.text(Number(data));
  30. });
  31. });
  32. }
  33. }
  34. /** AI 摘要(post.html 内联脚本,pjax 后由 executeScripts 触发) */
  35. function reinitAISummary() {
  36. if (typeof ai_gen === 'function' && $('#ai-output').length) {
  37. try { ai_gen(); } catch (e) { /* ignore */ }
  38. }
  39. }
  40. /** 代码块复制按钮 */
  41. function reinitCopyButtons() {
  42. $('.copy').remove();
  43. $('div.highlight').each(function () {
  44. var $block = $(this);
  45. var $btn = $('<button>', { class: 'copy', type: 'button', text: '📋' });
  46. $block.append($btn);
  47. $btn.on('click', function () {
  48. var code = $btn.siblings('pre').find('code').text().trim();
  49. navigator.clipboard.writeText(code)
  50. .then(function () { $btn.text('✅'); })
  51. .catch(function () { $btn.text('❌'); })
  52. .finally(function () { setTimeout(function () { $btn.text('📋'); }, 1500); });
  53. });
  54. });
  55. }
  56. /** 关键词高亮 */
  57. function reinitHighlight() {
  58. var keyword = new URLSearchParams(window.location.search).get('kw');
  59. if (!keyword) return;
  60. keyword = keyword.trim();
  61. if (!keyword) return;
  62. var escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  63. var regex = new RegExp('(' + escaped + ')', 'gi');
  64. var escapeHTML = function (str) {
  65. return str.replace(/[&<>"']/g, function (t) {
  66. return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[t] || t;
  67. });
  68. };
  69. function walk(node) {
  70. $(node).contents().each(function () {
  71. if (this.nodeType === Node.TEXT_NODE) {
  72. var $t = $(this);
  73. var text = escapeHTML($t.text());
  74. if (regex.test(text)) $t.replaceWith(text.replace(regex, '<mark>$1</mark>'));
  75. } else if (this.nodeType === Node.ELEMENT_NODE && !$(this).is('script, style, noscript, textarea')) {
  76. walk(this);
  77. }
  78. });
  79. }
  80. $('section').each(function () { walk(this); });
  81. }
  82. /** Google Analytics 页面浏览事件 */
  83. function trackPageView() {
  84. if (typeof gtag === 'function') {
  85. gtag('config', window._gaId || '', { page_path: window.location.pathname });
  86. }
  87. }
  88. /** Live2D 重初始化 */
  89. var _live2dSelectors = ['.post-link', '#search-input'];
  90. var _live2dDelegateBound = false;
  91. function reinitLive2d() {
  92. if (!window._live2d) return;
  93. var pathname = window.location.pathname;
  94. // 更新"想问这篇文章"相关状态(仅真正的文章页显示)
  95. $('#post_id').val(pathname);
  96. if ($(CONTAINER + ' #gitalk-container').length > 0) {
  97. $('.live_talk_input_name_body').show();
  98. } else {
  99. $('.live_talk_input_name_body').hide();
  100. $('#load_this').prop('checked', false);
  101. }
  102. // 音乐按钮:根据当前页面是否有 BGM 输入来显示/隐藏
  103. if (typeof window._live2d.initBGM === 'function') {
  104. window._live2d.initBGM();
  105. }
  106. // 事件委托绑定(只执行一次)
  107. if (!_live2dDelegateBound && typeof String.prototype.renderTip === 'function') {
  108. var selector = CONTAINER + ' ' + _live2dSelectors.join(', ' + CONTAINER + ' ');
  109. $(document).on('mouseover._live2d_pjax', selector, function (e) {
  110. var $el = $(e.currentTarget || e.target);
  111. if ($el.is('.post-link')) {
  112. window._live2d.showMessage('要看看 ' + $el.text() + ' 么?', 3000);
  113. } else if ($el.is('#search-input')) {
  114. window._live2d.showMessage('在找什么东西呢,需要帮忙吗?', 3000);
  115. }
  116. });
  117. $(document).on('mouseout._live2d_pjax', selector, function () {
  118. if (window._live2d.showHitokoto) window._live2d.showHitokoto();
  119. });
  120. _live2dDelegateBound = true;
  121. }
  122. // 欢迎语
  123. if (typeof window._live2d.showMessage === 'function') {
  124. window._live2d.showMessage(getWelcomeText(pathname), 6000);
  125. }
  126. }
  127. // ========== PJAX 导航 ==========
  128. /** PJAX 完成后的统一处理 */
  129. function doPjaxComplete() {
  130. $('body').removeClass('pjax-loading');
  131. // 清理可能残留的浮层(如推荐文章 tooltip,hover 后点击跳转时 mouseleave 来不及触发)
  132. $('.content-tooltip').remove();
  133. onPjaxComplete();
  134. }
  135. /** 暴露给模板内 onclick/onchange 调用的导航函数 */
  136. window.go = function (url) {
  137. $.pjax({ url: url, ...PJAX_OPTS });
  138. };
  139. // ========== 初始化 ==========
  140. /** 每次 pjax 完成后执行所有重初始化 */
  141. function onPjaxComplete() {
  142. reinitVisitors();
  143. reinitCopyButtons();
  144. reinitHighlight();
  145. reinitAISummary();
  146. reinitLive2d();
  147. trackPageView();
  148. window.scrollTo(0, 0);
  149. }
  150. $(document).ready(function () {
  151. // 排除列表:外链、锚点、静态资源、Live2D 目录
  152. var exclude = ':not([target="_blank"]):not([href^="http"]):not([href^="//"])' +
  153. ':not([href^="mailto"]):not([href^="#"])' +
  154. ':not([href$=".xml"]):not([href$=".json"]):not([href$=".tgz"]):not([href$=".zip"])' +
  155. ':not([href^="/Live2dHistoire"])';
  156. $(document).pjax('a' + exclude, PJAX_OPTS.container, PJAX_OPTS);
  157. $(document).on('submit', 'form#search-input-all', function (e) {
  158. $.pjax.submit(e, PJAX_OPTS.container, PJAX_OPTS);
  159. });
  160. $(document).on('pjax:send', function () {
  161. $('body').addClass('pjax-loading');
  162. });
  163. $(document).on('pjax:complete', doPjaxComplete);
  164. $(document).on('pjax:error', function (xhr, textStatus, error) {
  165. console.warn('[pjax] error, fallback:', error);
  166. });
  167. // 首次加载初始化
  168. reinitCopyButtons();
  169. });
  170. })(jQuery);