diff --git a/assets/vendor/external/css-fallback.css b/assets/vendor/external/css-fallback.css
new file mode 100644
index 0000000..47d1eda
--- /dev/null
+++ b/assets/vendor/external/css-fallback.css
@@ -0,0 +1,300 @@
+/* CSS 备用系统 - 确保基本样式始终可用 */
+
+/* 重置样式 - 确保跨浏览器一致性 */
+* {
+ box-sizing: border-box;
+}
+
+html {
+ line-height: 1.15;
+ -webkit-text-size-adjust: 100%;
+}
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ font-size: 16px;
+ line-height: 1.6;
+ color: #333;
+ background-color: #fff;
+}
+
+/* 基本布局 */
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 15px;
+}
+
+.row {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0 -15px;
+}
+
+.col {
+ flex: 1;
+ padding: 0 15px;
+}
+
+/* 按钮样式 */
+.btn {
+ display: inline-block;
+ padding: 8px 16px;
+ margin: 4px 2px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ background-color: #f8f9fa;
+ color: #333;
+ text-decoration: none;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.btn:hover {
+ background-color: #e9ecef;
+ border-color: #adb5bd;
+}
+
+.btn-primary {
+ background-color: #007bff;
+ border-color: #007bff;
+ color: #fff;
+}
+
+.btn-primary:hover {
+ background-color: #0056b3;
+ border-color: #004085;
+}
+
+/* 卡片样式 */
+.card {
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ border-radius: 4px;
+ margin-bottom: 20px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.card-header {
+ padding: 12px 20px;
+ background-color: #f8f9fa;
+ border-bottom: 1px solid #dee2e6;
+ font-weight: bold;
+}
+
+.card-body {
+ padding: 20px;
+}
+
+/* 表单样式 */
+.form-control {
+ display: block;
+ width: 100%;
+ padding: 8px 12px;
+ font-size: 14px;
+ line-height: 1.5;
+ color: #495057;
+ background-color: #fff;
+ border: 1px solid #ced4da;
+ border-radius: 4px;
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+.form-control:focus {
+ border-color: #80bdff;
+ outline: 0;
+ box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
+}
+
+/* 导航样式 */
+.navbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 20px;
+ background-color: #f8f9fa;
+ border-bottom: 1px solid #dee2e6;
+}
+
+.navbar-brand {
+ font-size: 18px;
+ font-weight: bold;
+ text-decoration: none;
+ color: #333;
+}
+
+.nav {
+ display: flex;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.nav-item {
+ margin: 0 10px;
+}
+
+.nav-link {
+ color: #333;
+ text-decoration: none;
+ padding: 8px 12px;
+ border-radius: 4px;
+ transition: background-color 0.2s ease;
+}
+
+.nav-link:hover {
+ background-color: #e9ecef;
+}
+
+/* 模态框样式 */
+.modal {
+ display: none;
+ position: fixed;
+ z-index: 1000;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0.5);
+}
+
+.modal.show {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.modal-dialog {
+ background-color: #fff;
+ border-radius: 4px;
+ max-width: 500px;
+ width: 90%;
+ max-height: 90%;
+ overflow-y: auto;
+}
+
+.modal-header {
+ padding: 15px 20px;
+ border-bottom: 1px solid #dee2e6;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.modal-body {
+ padding: 20px;
+}
+
+.modal-footer {
+ padding: 15px 20px;
+ border-top: 1px solid #dee2e6;
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+/* 警告和错误样式 */
+.alert {
+ padding: 12px 20px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+
+.alert-warning {
+ color: #856404;
+ background-color: #fff3cd;
+ border-color: #ffeaa7;
+}
+
+.alert-danger {
+ color: #721c24;
+ background-color: #f8d7da;
+ border-color: #f5c6cb;
+}
+
+.alert-info {
+ color: #0c5460;
+ background-color: #d1ecf1;
+ border-color: #bee5eb;
+}
+
+/* 加载状态 */
+.loading {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ border: 3px solid #f3f3f3;
+ border-top: 3px solid #3498db;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .container {
+ padding: 0 10px;
+ }
+
+ .row {
+ margin: 0 -10px;
+ }
+
+ .col {
+ padding: 0 10px;
+ }
+
+ .navbar {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .nav {
+ flex-direction: column;
+ margin-top: 10px;
+ }
+
+ .nav-item {
+ margin: 5px 0;
+ }
+}
+
+/* 暗色模式支持 */
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #1a1a1a;
+ color: #e0e0e0;
+ }
+
+ .card {
+ background-color: #2d2d2d;
+ border-color: #404040;
+ }
+
+ .card-header {
+ background-color: #404040;
+ border-color: #555;
+ }
+
+ .form-control {
+ background-color: #2d2d2d;
+ border-color: #404040;
+ color: #e0e0e0;
+ }
+
+ .navbar {
+ background-color: #2d2d2d;
+ border-color: #404040;
+ }
+
+ .navbar-brand,
+ .nav-link {
+ color: #e0e0e0;
+ }
+}
\ No newline at end of file
diff --git a/assets/vendor/external/fonts/files/opensans-300-latin.woff2 b/assets/vendor/external/fonts/files/opensans-300-latin.woff2
new file mode 100644
index 0000000..b5d54e7
Binary files /dev/null and b/assets/vendor/external/fonts/files/opensans-300-latin.woff2 differ
diff --git a/assets/vendor/external/fonts/files/opensans-400-latin.woff2 b/assets/vendor/external/fonts/files/opensans-400-latin.woff2
new file mode 100644
index 0000000..b5d54e7
Binary files /dev/null and b/assets/vendor/external/fonts/files/opensans-400-latin.woff2 differ
diff --git a/assets/vendor/external/fonts/files/opensans-600-latin.woff2 b/assets/vendor/external/fonts/files/opensans-600-latin.woff2
new file mode 100644
index 0000000..f48dda7
Binary files /dev/null and b/assets/vendor/external/fonts/files/opensans-600-latin.woff2 differ
diff --git a/assets/vendor/external/fonts/font-fallback.css b/assets/vendor/external/fonts/font-fallback.css
new file mode 100644
index 0000000..62e623c
--- /dev/null
+++ b/assets/vendor/external/fonts/font-fallback.css
@@ -0,0 +1,72 @@
+/* 字体备用系统 - 确保在任何情况下都有合适的字体显示 */
+
+/* 全局字体备用栈 */
+:root {
+ --font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ --font-family-serif: Georgia, Cambria, "Times New Roman", Times, serif;
+ --font-family-mono: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ --font-family-chinese: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
+}
+
+/* 当 Open Sans 不可用时的备用 */
+body, .open-sans-fallback {
+ font-family: "Open Sans", var(--font-family-sans);
+}
+
+/* 当 Noto Serif SC 不可用时的备用 */
+.noto-serif-fallback {
+ font-family: "Noto Serif SC", var(--font-family-chinese), var(--font-family-serif);
+}
+
+/* 通用中文字体备用 */
+.chinese-text {
+ font-family: var(--font-family-chinese);
+}
+
+/* 确保代码字体始终可用 */
+code, pre, .monospace {
+ font-family: var(--font-family-mono);
+}
+
+/* 字体加载失败时的样式调整 */
+.font-loading-error {
+ font-family: var(--font-family-sans) !important;
+}
+
+.font-loading-error::before {
+ content: "";
+ display: inline-block;
+ width: 0;
+ height: 0;
+}
+
+/* 响应式字体大小 */
+@media (max-width: 768px) {
+ body {
+ font-size: 14px;
+ line-height: 1.5;
+ }
+}
+
+@media (min-width: 769px) {
+ body {
+ font-size: 16px;
+ line-height: 1.6;
+ }
+}
+
+/* 确保图标字体备用 */
+.fa, .fas, .far, .fal, .fab {
+ font-family: "Font Awesome 5 Free", "Font Awesome 5 Pro", "FontAwesome", sans-serif;
+}
+
+/* 当图标字体不可用时显示文字 */
+.fa-home::after { content: "首页"; }
+.fa-user::after { content: "用户"; }
+.fa-search::after { content: "搜索"; }
+.fa-menu::after { content: "菜单"; }
+.fa-close::after { content: "关闭"; }
+.fa-arrow-left::after { content: "←"; }
+.fa-arrow-right::after { content: "→"; }
+.fa-arrow-up::after { content: "↑"; }
+.fa-arrow-down::after { content: "↓"; }
\ No newline at end of file
diff --git a/assets/vendor/external/fonts/open-sans.css b/assets/vendor/external/fonts/open-sans.css
index 9377d95..ff404fa 100644
--- a/assets/vendor/external/fonts/open-sans.css
+++ b/assets/vendor/external/fonts/open-sans.css
@@ -1,32 +1,50 @@
-/* Open Sans Font - Local Fallback */
+/* Open Sans Font - 完整本地备用版本 */
+
+/* 300 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-display: swap;
- src: local('Open Sans Light'), local('OpenSans-Light');
+ src: url('files/opensans-300-latin.woff2') format('woff2'),
+ local('Open Sans Light'), local('OpenSans-Light');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
+/* 400 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-display: swap;
- src: local('Open Sans Regular'), local('OpenSans-Regular');
+ src: url('files/opensans-400-latin.woff2') format('woff2'),
+ local('Open Sans Regular'), local('OpenSans-Regular');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
+/* 600 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-display: swap;
- src: local('Open Sans SemiBold'), local('OpenSans-SemiBold');
+ src: url('files/opensans-600-latin.woff2') format('woff2'),
+ local('Open Sans SemiBold'), local('OpenSans-SemiBold');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
+/* 700 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
- src: local('Open Sans Bold'), local('OpenSans-Bold');
+ src: url('files/opensans-700-latin.woff2') format('woff2'),
+ local('Open Sans Bold'), local('OpenSans-Bold');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/* 备用字体栈 */
+body, .font-sans-serif {
+ font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
}
\ No newline at end of file
diff --git a/assets/vendor/external/js-fallback.js b/assets/vendor/external/js-fallback.js
new file mode 100644
index 0000000..b200338
--- /dev/null
+++ b/assets/vendor/external/js-fallback.js
@@ -0,0 +1,328 @@
+/* JavaScript 功能备用系统 - 确保基本功能始终可用 */
+(function() {
+ 'use strict';
+
+ // 检查并提供基本的 JavaScript 功能
+ window.ArgonFallback = {
+ // 初始化备用系统
+ init: function() {
+ this.checkDependencies();
+ this.provideFallbacks();
+ this.bindEvents();
+ console.log('JavaScript 备用系统已启动');
+ },
+
+ // 检查依赖项
+ checkDependencies: function() {
+ var missing = [];
+
+ // 检查 jQuery
+ if (typeof jQuery === 'undefined') {
+ missing.push('jQuery');
+ this.provideJQueryFallback();
+ }
+
+ // 检查其他关键库
+ if (typeof bootstrap === 'undefined') {
+ missing.push('Bootstrap');
+ }
+
+ if (missing.length > 0) {
+ console.warn('缺少依赖项:', missing.join(', '));
+ }
+ },
+
+ // 提供 jQuery 基本功能备用
+ provideJQueryFallback: function() {
+ window.$ = window.jQuery = function(selector) {
+ return new jQueryFallback(selector);
+ };
+
+ // 扩展 jQuery 对象
+ $.fn = jQueryFallback.prototype;
+ $.extend = function(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+ return target;
+ };
+
+ console.log('jQuery 备用版本已加载');
+ },
+
+ // 提供其他备用功能
+ provideFallbacks: function() {
+ // 模态框功能
+ this.provideModalFallback();
+
+ // 工具提示功能
+ this.provideTooltipFallback();
+
+ // 下拉菜单功能
+ this.provideDropdownFallback();
+ },
+
+ // 模态框备用
+ provideModalFallback: function() {
+ window.showModal = function(content, title) {
+ var modal = document.createElement('div');
+ modal.className = 'modal show';
+ modal.innerHTML =
+ '
' +
+ '' +
+ '
' + content + '
' +
+ '' +
+ '
';
+ document.body.appendChild(modal);
+
+ // 点击背景关闭
+ modal.addEventListener('click', function(e) {
+ if (e.target === modal) {
+ modal.remove();
+ }
+ });
+ };
+ },
+
+ // 工具提示备用
+ provideTooltipFallback: function() {
+ document.addEventListener('mouseover', function(e) {
+ var element = e.target;
+ var tooltip = element.getAttribute('title') || element.getAttribute('data-tooltip');
+
+ if (tooltip && !element.querySelector('.tooltip-fallback')) {
+ var tooltipEl = document.createElement('div');
+ tooltipEl.className = 'tooltip-fallback';
+ tooltipEl.textContent = tooltip;
+ tooltipEl.style.cssText =
+ 'position: absolute; background: #333; color: #fff; padding: 4px 8px; ' +
+ 'border-radius: 4px; font-size: 12px; z-index: 1000; pointer-events: none;';
+
+ document.body.appendChild(tooltipEl);
+
+ var rect = element.getBoundingClientRect();
+ tooltipEl.style.left = rect.left + 'px';
+ tooltipEl.style.top = (rect.top - tooltipEl.offsetHeight - 5) + 'px';
+
+ element.addEventListener('mouseleave', function() {
+ if (tooltipEl.parentNode) {
+ tooltipEl.parentNode.removeChild(tooltipEl);
+ }
+ });
+ }
+ });
+ },
+
+ // 下拉菜单备用
+ provideDropdownFallback: function() {
+ document.addEventListener('click', function(e) {
+ var toggle = e.target.closest('[data-toggle="dropdown"]');
+ if (toggle) {
+ e.preventDefault();
+ var menu = toggle.nextElementSibling;
+ if (menu && menu.classList.contains('dropdown-menu')) {
+ menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
+ }
+ }
+
+ // 关闭其他下拉菜单
+ var openMenus = document.querySelectorAll('.dropdown-menu[style*="block"]');
+ openMenus.forEach(function(menu) {
+ if (!menu.contains(e.target) && !menu.previousElementSibling.contains(e.target)) {
+ menu.style.display = 'none';
+ }
+ });
+ });
+ },
+
+ // 绑定事件
+ bindEvents: function() {
+ var self = this;
+
+ // 监听错误事件
+ window.addEventListener('error', function(e) {
+ console.warn('JavaScript 错误:', e.message);
+ self.handleError(e);
+ });
+
+ // 监听未处理的 Promise 拒绝
+ window.addEventListener('unhandledrejection', function(e) {
+ console.warn('未处理的 Promise 拒绝:', e.reason);
+ });
+ },
+
+ // 错误处理
+ handleError: function(error) {
+ // 尝试恢复基本功能
+ if (error.message.includes('jQuery') || error.message.includes('$')) {
+ this.provideJQueryFallback();
+ }
+ }
+ };
+
+ // jQuery 备用实现
+ function jQueryFallback(selector) {
+ this.elements = [];
+
+ if (typeof selector === 'string') {
+ this.elements = Array.from(document.querySelectorAll(selector));
+ } else if (selector && selector.nodeType) {
+ this.elements = [selector];
+ } else if (selector && typeof selector === 'object' && selector.length !== undefined) {
+ this.elements = Array.from(selector);
+ }
+
+ this.length = this.elements.length;
+ return this;
+ }
+
+ // jQuery 方法实现
+ jQueryFallback.prototype = {
+ each: function(callback) {
+ this.elements.forEach(callback);
+ return this;
+ },
+
+ on: function(event, handler) {
+ this.elements.forEach(function(el) {
+ el.addEventListener(event, handler);
+ });
+ return this;
+ },
+
+ off: function(event, handler) {
+ this.elements.forEach(function(el) {
+ el.removeEventListener(event, handler);
+ });
+ return this;
+ },
+
+ click: function(handler) {
+ if (handler) {
+ return this.on('click', handler);
+ } else {
+ this.elements.forEach(function(el) {
+ el.click();
+ });
+ return this;
+ }
+ },
+
+ addClass: function(className) {
+ this.elements.forEach(function(el) {
+ el.classList.add(className);
+ });
+ return this;
+ },
+
+ removeClass: function(className) {
+ this.elements.forEach(function(el) {
+ el.classList.remove(className);
+ });
+ return this;
+ },
+
+ toggleClass: function(className) {
+ this.elements.forEach(function(el) {
+ el.classList.toggle(className);
+ });
+ return this;
+ },
+
+ hasClass: function(className) {
+ return this.elements.some(function(el) {
+ return el.classList.contains(className);
+ });
+ },
+
+ html: function(content) {
+ if (content !== undefined) {
+ this.elements.forEach(function(el) {
+ el.innerHTML = content;
+ });
+ return this;
+ } else {
+ return this.elements[0] ? this.elements[0].innerHTML : '';
+ }
+ },
+
+ text: function(content) {
+ if (content !== undefined) {
+ this.elements.forEach(function(el) {
+ el.textContent = content;
+ });
+ return this;
+ } else {
+ return this.elements[0] ? this.elements[0].textContent : '';
+ }
+ },
+
+ hide: function() {
+ this.elements.forEach(function(el) {
+ el.style.display = 'none';
+ });
+ return this;
+ },
+
+ show: function() {
+ this.elements.forEach(function(el) {
+ el.style.display = '';
+ });
+ return this;
+ },
+
+ fadeIn: function(duration) {
+ this.elements.forEach(function(el) {
+ el.style.opacity = '0';
+ el.style.display = '';
+ var start = Date.now();
+ var timer = setInterval(function() {
+ var progress = (Date.now() - start) / (duration || 400);
+ if (progress >= 1) {
+ el.style.opacity = '1';
+ clearInterval(timer);
+ } else {
+ el.style.opacity = progress;
+ }
+ }, 16);
+ });
+ return this;
+ },
+
+ fadeOut: function(duration) {
+ this.elements.forEach(function(el) {
+ var start = Date.now();
+ var startOpacity = parseFloat(getComputedStyle(el).opacity);
+ var timer = setInterval(function() {
+ var progress = (Date.now() - start) / (duration || 400);
+ if (progress >= 1) {
+ el.style.display = 'none';
+ clearInterval(timer);
+ } else {
+ el.style.opacity = startOpacity * (1 - progress);
+ }
+ }, 16);
+ });
+ return this;
+ }
+ };
+
+ // 页面加载完成后初始化
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', function() {
+ ArgonFallback.init();
+ });
+ } else {
+ ArgonFallback.init();
+ }
+})();
\ No newline at end of file
diff --git a/assets/vendor/external/katex/auto-render.min.js b/assets/vendor/external/katex/auto-render.min.js
new file mode 100644
index 0000000..96497e8
--- /dev/null
+++ b/assets/vendor/external/katex/auto-render.min.js
@@ -0,0 +1,54 @@
+/* KaTeX Auto-Render - Local Fallback */
+(function() {
+ 'use strict';
+
+ console.warn('KaTeX Auto-Render 本地备用版本 - 功能受限');
+
+ window.renderMathInElement = function(element, options) {
+ console.warn('KaTeX 自动渲染功能不可用 - 使用备用版本');
+
+ options = options || {};
+ var delimiters = options.delimiters || [
+ {left: "$$", right: "$$", display: true},
+ {left: "$", right: "$", display: false},
+ {left: "\\(", right: "\\)", display: false},
+ {left: "\\[", right: "\\]", display: true}
+ ];
+
+ if (!element) {
+ element = document.body;
+ }
+
+ // 简单的文本替换处理
+ var walker = document.createTreeWalker(
+ element,
+ NodeFilter.SHOW_TEXT,
+ null,
+ false
+ );
+
+ var textNodes = [];
+ var node;
+ while (node = walker.nextNode()) {
+ textNodes.push(node);
+ }
+
+ textNodes.forEach(function(textNode) {
+ var text = textNode.textContent;
+ var hasMatch = false;
+
+ delimiters.forEach(function(delimiter) {
+ if (text.includes(delimiter.left) && text.includes(delimiter.right)) {
+ hasMatch = true;
+ }
+ });
+
+ if (hasMatch) {
+ var span = document.createElement('span');
+ span.innerHTML = text.replace(/\$([^$]+)\$/g,
+ '$1');
+ textNode.parentNode.replaceChild(span, textNode);
+ }
+ });
+ };
+})();
\ No newline at end of file
diff --git a/assets/vendor/external/katex/katex.min.css b/assets/vendor/external/katex/katex.min.css
new file mode 100644
index 0000000..850147d
--- /dev/null
+++ b/assets/vendor/external/katex/katex.min.css
@@ -0,0 +1,47 @@
+/* KaTeX CSS - Local Fallback */
+.katex {
+ font: normal 1.21em KaTeX_Main, "Times New Roman", serif;
+ line-height: 1.2;
+ text-indent: 0;
+ text-rendering: auto;
+ font-style: italic;
+ color: #666;
+}
+
+.katex * {
+ -ms-high-contrast-adjust: none !important;
+}
+
+.katex .katex-version::after {
+ content: "本地备用版本";
+}
+
+.katex .katex-mathml {
+ position: absolute;
+ clip: rect(1px, 1px, 1px, 1px);
+ padding: 0;
+ border: 0;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+}
+
+.katex-display {
+ display: block;
+ margin: 1em 0;
+ text-align: center;
+}
+
+.katex-display > .katex {
+ display: block;
+ text-align: center;
+ white-space: nowrap;
+}
+
+/* 备用样式提示 */
+.katex::before {
+ content: "[数学公式渲染服务不可用] ";
+ font-size: 0.8em;
+ color: #999;
+ font-style: normal;
+}
\ No newline at end of file
diff --git a/assets/vendor/external/katex/katex.min.js b/assets/vendor/external/katex/katex.min.js
new file mode 100644
index 0000000..7d844ed
--- /dev/null
+++ b/assets/vendor/external/katex/katex.min.js
@@ -0,0 +1,27 @@
+/* KaTeX JS - Local Fallback */
+(function() {
+ 'use strict';
+
+ console.warn('KaTeX 本地备用版本 - 功能受限');
+
+ // 创建简化的 KaTeX 对象
+ window.katex = {
+ render: function(tex, element, options) {
+ console.warn('KaTeX 渲染功能不可用 - 使用备用版本');
+ if (element) {
+ element.innerHTML = '' +
+ tex + '';
+ element.className = (element.className || '') + ' katex';
+ }
+ },
+
+ renderToString: function(tex, options) {
+ console.warn('KaTeX 渲染功能不可用 - 使用备用版本');
+ return '' +
+ tex + '';
+ }
+ };
+
+ // 版本信息
+ window.katex.__version = "0.11.1-fallback";
+})();
\ No newline at end of file
diff --git a/assets/vendor/external/logger.js b/assets/vendor/external/logger.js
new file mode 100644
index 0000000..aa32f4f
--- /dev/null
+++ b/assets/vendor/external/logger.js
@@ -0,0 +1,266 @@
+/* Argon 内部日志系统 - 仅管理员可见 */
+(function() {
+ 'use strict';
+
+ window.ArgonLogger = {
+ // 配置
+ config: {
+ maxLogs: 1000,
+ enableConsole: false, // 默认关闭控制台输出
+ enableStorage: true,
+ storageKey: 'argon_internal_logs'
+ },
+
+ // 日志级别
+ levels: {
+ DEBUG: 0,
+ INFO: 1,
+ WARN: 2,
+ ERROR: 3,
+ CRITICAL: 4
+ },
+
+ // 内部日志存储
+ logs: [],
+
+ // 初始化
+ init: function() {
+ this.checkAdminStatus();
+ this.loadStoredLogs();
+ this.bindEvents();
+ },
+
+ // 检查管理员状态
+ checkAdminStatus: function() {
+ // 检查是否为管理员(通过PHP传递的全局变量)
+ if (typeof window.argonUserRole !== 'undefined' && window.argonUserRole === 'administrator') {
+ this.config.enableConsole = true;
+ this.log('管理员模式:控制台日志已启用', 'INFO');
+ } else {
+ this.config.enableConsole = false;
+ }
+ },
+
+ // 加载存储的日志
+ loadStoredLogs: function() {
+ if (this.config.enableStorage && localStorage) {
+ try {
+ var stored = localStorage.getItem(this.config.storageKey);
+ if (stored) {
+ this.logs = JSON.parse(stored);
+ }
+ } catch (e) {
+ // 静默处理存储错误
+ }
+ }
+ },
+
+ // 保存日志到存储
+ saveLogsToStorage: function() {
+ if (this.config.enableStorage && localStorage) {
+ try {
+ // 只保留最新的日志
+ var logsToSave = this.logs.slice(-this.config.maxLogs);
+ localStorage.setItem(this.config.storageKey, JSON.stringify(logsToSave));
+ } catch (e) {
+ // 静默处理存储错误
+ }
+ }
+ },
+
+ // 记录日志
+ log: function(message, level, data) {
+ level = level || 'INFO';
+ var timestamp = new Date().toISOString();
+ var logEntry = {
+ timestamp: timestamp,
+ level: level,
+ message: message,
+ data: data || null,
+ url: window.location.href,
+ userAgent: navigator.userAgent.substring(0, 100) // 截取前100字符
+ };
+
+ // 添加到内部日志
+ this.logs.push(logEntry);
+
+ // 限制日志数量
+ if (this.logs.length > this.config.maxLogs) {
+ this.logs = this.logs.slice(-this.config.maxLogs);
+ }
+
+ // 保存到存储
+ this.saveLogsToStorage();
+
+ // 仅管理员显示控制台日志
+ if (this.config.enableConsole) {
+ var consoleMethod = this.getConsoleMethod(level);
+ consoleMethod('[Argon] ' + message, data || '');
+ }
+
+ // 关键错误需要特殊处理
+ if (level === 'CRITICAL') {
+ this.handleCriticalError(logEntry);
+ }
+ },
+
+ // 获取对应的控制台方法
+ getConsoleMethod: function(level) {
+ switch (level) {
+ case 'DEBUG':
+ return console.debug || console.log;
+ case 'INFO':
+ return console.info || console.log;
+ case 'WARN':
+ return console.warn || console.log;
+ case 'ERROR':
+ case 'CRITICAL':
+ return console.error || console.log;
+ default:
+ return console.log;
+ }
+ },
+
+ // 处理关键错误
+ handleCriticalError: function(logEntry) {
+ // 关键错误需要通知用户联系站长
+ if (this.config.enableConsole) {
+ console.error('发生关键错误,请联系站长处理:', logEntry.message);
+ }
+
+ // 可以在这里添加错误上报逻辑
+ this.reportCriticalError(logEntry);
+ },
+
+ // 上报关键错误
+ reportCriticalError: function(logEntry) {
+ // 这里可以实现错误上报到服务器的逻辑
+ // 暂时只记录到本地存储
+ try {
+ var criticalLogs = JSON.parse(localStorage.getItem('argon_critical_errors') || '[]');
+ criticalLogs.push(logEntry);
+ localStorage.setItem('argon_critical_errors', JSON.stringify(criticalLogs.slice(-50)));
+ } catch (e) {
+ // 静默处理
+ }
+ },
+
+ // 获取日志(仅管理员)
+ getLogs: function(level, limit) {
+ if (!this.config.enableConsole) {
+ return []; // 非管理员返回空数组
+ }
+
+ var filteredLogs = this.logs;
+
+ if (level) {
+ filteredLogs = this.logs.filter(function(log) {
+ return log.level === level;
+ });
+ }
+
+ if (limit) {
+ filteredLogs = filteredLogs.slice(-limit);
+ }
+
+ return filteredLogs;
+ },
+
+ // 清除日志(仅管理员)
+ clearLogs: function() {
+ if (!this.config.enableConsole) {
+ return false;
+ }
+
+ this.logs = [];
+ if (localStorage) {
+ localStorage.removeItem(this.config.storageKey);
+ localStorage.removeItem('argon_critical_errors');
+ }
+ return true;
+ },
+
+ // 导出日志(仅管理员)
+ exportLogs: function() {
+ if (!this.config.enableConsole) {
+ return null;
+ }
+
+ var exportData = {
+ timestamp: new Date().toISOString(),
+ site: window.location.origin,
+ logs: this.logs,
+ criticalErrors: JSON.parse(localStorage.getItem('argon_critical_errors') || '[]')
+ };
+
+ return JSON.stringify(exportData, null, 2);
+ },
+
+ // 绑定事件
+ bindEvents: function() {
+ var self = this;
+
+ // 监听页面错误
+ window.addEventListener('error', function(e) {
+ self.log('JavaScript错误: ' + e.message, 'ERROR', {
+ filename: e.filename,
+ lineno: e.lineno,
+ colno: e.colno,
+ stack: e.error ? e.error.stack : null
+ });
+ });
+
+ // 监听未处理的Promise拒绝
+ window.addEventListener('unhandledrejection', function(e) {
+ self.log('未处理的Promise拒绝: ' + e.reason, 'ERROR', {
+ reason: e.reason,
+ promise: e.promise
+ });
+ });
+
+ // 监听资源加载错误
+ window.addEventListener('error', function(e) {
+ if (e.target && e.target !== window) {
+ var resourceType = e.target.tagName || 'unknown';
+ var resourceUrl = e.target.src || e.target.href || 'unknown';
+ self.log('资源加载失败: ' + resourceType + ' - ' + resourceUrl, 'WARN');
+ }
+ }, true);
+ },
+
+ // 便捷方法
+ debug: function(message, data) {
+ this.log(message, 'DEBUG', data);
+ },
+
+ info: function(message, data) {
+ this.log(message, 'INFO', data);
+ },
+
+ warn: function(message, data) {
+ this.log(message, 'WARN', data);
+ },
+
+ error: function(message, data) {
+ this.log(message, 'ERROR', data);
+ },
+
+ critical: function(message, data) {
+ this.log(message, 'CRITICAL', data);
+ }
+ };
+
+ // 自动初始化
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', function() {
+ ArgonLogger.init();
+ });
+ } else {
+ ArgonLogger.init();
+ }
+
+ // 为管理员提供全局访问
+ if (typeof window.argonUserRole !== 'undefined' && window.argonUserRole === 'administrator') {
+ window.ArgonLogs = ArgonLogger;
+ }
+})();
\ No newline at end of file
diff --git a/assets/vendor/external/mathjax2/MathJax.js b/assets/vendor/external/mathjax2/MathJax.js
new file mode 100644
index 0000000..018902c
--- /dev/null
+++ b/assets/vendor/external/mathjax2/MathJax.js
@@ -0,0 +1,62 @@
+/* MathJax 2 - Local Fallback */
+(function() {
+ 'use strict';
+
+ console.warn('MathJax 2 本地备用版本 - 功能受限');
+
+ // 创建简化的 MathJax 对象
+ window.MathJax = window.MathJax || {};
+
+ window.MathJax.Hub = {
+ Config: function(config) {
+ console.log('MathJax 2 配置已设置(备用版本)');
+ this.config = config;
+ },
+
+ Queue: function(callback) {
+ if (typeof callback === 'function') {
+ setTimeout(callback, 100);
+ }
+ return this;
+ },
+
+ Typeset: function(element) {
+ console.warn('MathJax 2 渲染功能不可用 - 使用备用版本');
+ // 简单的样式处理
+ var mathElements = element ?
+ element.querySelectorAll('script[type*="math"]') :
+ document.querySelectorAll('script[type*="math"]');
+
+ mathElements.forEach(function(script) {
+ var span = document.createElement('span');
+ span.textContent = script.textContent;
+ span.style.fontStyle = 'italic';
+ span.style.color = '#666';
+ span.title = '数学公式渲染服务不可用';
+ script.parentNode.insertBefore(span, script);
+ script.style.display = 'none';
+ });
+ },
+
+ Register: {
+ StartupHook: function(hook, callback) {
+ if (typeof callback === 'function') {
+ setTimeout(callback, 100);
+ }
+ }
+ }
+ };
+
+ // 自动配置
+ if (window.MathJax.Hub.Config) {
+ window.MathJax.Hub.Config({
+ messageStyle: "none",
+ tex2jax: {
+ inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],
+ displayMath: [['$$','$$']],
+ processEscapes: true,
+ skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
+ }
+ });
+ }
+})();
\ No newline at end of file
diff --git a/assets/vendor/external/mathjax3/tex-chtml-full.js b/assets/vendor/external/mathjax3/tex-chtml-full.js
new file mode 100644
index 0000000..78f5fad
--- /dev/null
+++ b/assets/vendor/external/mathjax3/tex-chtml-full.js
@@ -0,0 +1,61 @@
+/* MathJax 3 - Local Fallback */
+(function() {
+ 'use strict';
+
+ console.warn('MathJax 3 本地备用版本 - 功能受限');
+
+ // 创建简化的 MathJax 对象
+ window.MathJax = window.MathJax || {
+ tex: {
+ inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],
+ displayMath: [['$$','$$']],
+ processEscapes: true,
+ packages: {'[+]': ['noerrors']}
+ },
+ options: {
+ skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
+ ignoreHtmlClass: 'tex2jax_ignore',
+ processHtmlClass: 'tex2jax_process'
+ },
+ loader: {
+ load: ['[tex]/noerrors']
+ },
+ startup: {
+ ready: function() {
+ console.log('MathJax 3 备用版本已就绪');
+ MathJax.startup.defaultReady();
+ },
+ defaultReady: function() {
+ // 简化的渲染函数
+ MathJax.typesetPromise = function(elements) {
+ return new Promise(function(resolve) {
+ console.warn('MathJax 渲染功能不可用 - 使用备用版本');
+ resolve();
+ });
+ };
+ }
+ }
+ };
+
+ // 简化的渲染函数
+ window.MathJax.typesetPromise = function(elements) {
+ return new Promise(function(resolve) {
+ console.warn('数学公式渲染服务不可用');
+ // 尝试简单的文本替换
+ var mathElements = document.querySelectorAll('.tex2jax_process, .MathJax');
+ mathElements.forEach(function(el) {
+ if (el.textContent.includes('$')) {
+ el.style.fontStyle = 'italic';
+ el.style.color = '#666';
+ el.title = '数学公式渲染服务不可用';
+ }
+ });
+ resolve();
+ });
+ };
+
+ // 触发启动
+ if (MathJax.startup && MathJax.startup.ready) {
+ MathJax.startup.ready();
+ }
+})();
\ No newline at end of file
diff --git a/assets/vendor/external/resource-loader.js b/assets/vendor/external/resource-loader.js
index 1aff9dd..86672b1 100644
--- a/assets/vendor/external/resource-loader.js
+++ b/assets/vendor/external/resource-loader.js
@@ -8,37 +8,131 @@
'fonts.googleapis.com/css?family=Open+Sans': '/wp-content/themes/argon/assets/vendor/external/fonts/open-sans.css',
'fonts.googleapis.com/css?family=Noto+Serif+SC': '/wp-content/themes/argon/assets/vendor/external/fonts/noto-serif-sc.css',
'static.geetest.com/v4/gt4.js': '/wp-content/themes/argon/assets/vendor/external/geetest/gt4.js',
- 'cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js': '/wp-content/themes/argon/assets/vendor/external/qrcode/qrcode.min.js'
+ 'cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js': '/wp-content/themes/argon/assets/vendor/external/qrcode/qrcode.min.js',
+ 'cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js': '/wp-content/themes/argon/assets/vendor/external/mathjax3/tex-chtml-full.js',
+ 'cdn.jsdelivr.net/npm/mathjax@2.7.5/MathJax.js': '/wp-content/themes/argon/assets/vendor/external/mathjax2/MathJax.js',
+ 'cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css': '/wp-content/themes/argon/assets/vendor/external/katex/katex.min.css',
+ 'cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js': '/wp-content/themes/argon/assets/vendor/external/katex/katex.min.js',
+ 'cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js': '/wp-content/themes/argon/assets/vendor/external/katex/auto-render.min.js'
+ },
+
+ // 配置选项
+ config: {
+ timeout: 5000,
+ retryCount: 2,
+ enableLogging: true,
+ enableFallbackCSS: true,
+ enableFallbackJS: true
+ },
+
+ // 初始化
+ init: function() {
+ this.loadFallbackSystems();
+ this.bindGlobalErrorHandlers();
+ this.log('资源加载器已初始化');
+ },
+
+ // 加载备用系统
+ loadFallbackSystems: function() {
+ if (this.config.enableFallbackCSS) {
+ this.loadCSS('/wp-content/themes/argon/assets/vendor/external/css-fallback.css');
+ this.loadCSS('/wp-content/themes/argon/assets/vendor/external/fonts/font-fallback.css');
+ }
+
+ if (this.config.enableFallbackJS) {
+ this.loadJS('/wp-content/themes/argon/assets/vendor/external/js-fallback.js');
+ this.loadJS('/wp-content/themes/argon/assets/vendor/external/resource-monitor.js');
+ }
+ },
+
+ // 绑定全局错误处理
+ bindGlobalErrorHandlers: function() {
+ var self = this;
+
+ // 监听资源加载错误
+ window.addEventListener('error', function(e) {
+ if (e.target && (e.target.tagName === 'SCRIPT' || e.target.tagName === 'LINK')) {
+ self.handleResourceError(e.target);
+ }
+ }, true);
+
+ // 监听未处理的Promise拒绝
+ window.addEventListener('unhandledrejection', function(e) {
+ if (typeof ArgonLogger !== 'undefined') {
+ ArgonLogger.warn('未处理的Promise拒绝: ' + e.reason);
+ }
+ });
+ },
+
+ // 处理资源加载错误
+ handleResourceError: function(element) {
+ var url = element.src || element.href;
+ if (url) {
+ if (typeof ArgonLogger !== 'undefined') {
+ ArgonLogger.warn('资源加载失败: ' + url);
+ }
+ var fallbackUrl = this.getFallbackUrl(url);
+ if (fallbackUrl) {
+ this.log('尝试加载备用资源: ' + fallbackUrl);
+ if (element.tagName === 'SCRIPT') {
+ this.loadJS(fallbackUrl);
+ } else if (element.tagName === 'LINK') {
+ this.loadCSS(fallbackUrl);
+ }
+ }
+ }
+ },
+
+ // 获取备用URL
+ getFallbackUrl: function(url) {
+ for (var key in this.resourceMap) {
+ if (url.includes(key)) {
+ return this.resourceMap[key];
+ }
+ }
+ return null;
},
// 加载CSS资源
loadCSS: function(url, fallbackUrl) {
+ var self = this;
return new Promise(function(resolve, reject) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
var timeout = setTimeout(function() {
- console.warn('CSS资源加载超时,使用本地备用:', url);
+ self.log('CSS资源加载超时: ' + url);
if (fallbackUrl) {
- ArgonResourceLoader.loadCSS(fallbackUrl).then(resolve).catch(reject);
+ self.loadCSS(fallbackUrl).then(resolve).catch(reject);
} else {
- reject(new Error('CSS加载失败且无备用资源'));
+ var autoFallback = self.getFallbackUrl(url);
+ if (autoFallback) {
+ self.loadCSS(autoFallback).then(resolve).catch(reject);
+ } else {
+ reject(new Error('CSS加载失败且无备用资源'));
+ }
}
- }, 5000);
+ }, self.config.timeout);
link.onload = function() {
clearTimeout(timeout);
+ self.log('CSS资源加载成功: ' + url);
resolve();
};
link.onerror = function() {
clearTimeout(timeout);
- console.warn('CSS资源加载失败,使用本地备用:', url);
+ self.log('CSS资源加载失败: ' + url);
if (fallbackUrl) {
- ArgonResourceLoader.loadCSS(fallbackUrl).then(resolve).catch(reject);
+ self.loadCSS(fallbackUrl).then(resolve).catch(reject);
} else {
- reject(new Error('CSS加载失败且无备用资源'));
+ var autoFallback = self.getFallbackUrl(url);
+ if (autoFallback) {
+ self.loadCSS(autoFallback).then(resolve).catch(reject);
+ } else {
+ reject(new Error('CSS加载失败且无备用资源'));
+ }
}
};
@@ -48,32 +142,44 @@
// 加载JS资源
loadJS: function(url, fallbackUrl) {
+ var self = this;
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = url;
script.async = true;
var timeout = setTimeout(function() {
- console.warn('JS资源加载超时,使用本地备用:', url);
+ self.log('JS资源加载超时: ' + url);
if (fallbackUrl) {
- ArgonResourceLoader.loadJS(fallbackUrl).then(resolve).catch(reject);
+ self.loadJS(fallbackUrl).then(resolve).catch(reject);
} else {
- reject(new Error('JS加载失败且无备用资源'));
+ var autoFallback = self.getFallbackUrl(url);
+ if (autoFallback) {
+ self.loadJS(autoFallback).then(resolve).catch(reject);
+ } else {
+ reject(new Error('JS加载失败且无备用资源'));
+ }
}
- }, 5000);
+ }, self.config.timeout);
script.onload = function() {
clearTimeout(timeout);
+ self.log('JS资源加载成功: ' + url);
resolve();
};
script.onerror = function() {
clearTimeout(timeout);
- console.warn('JS资源加载失败,使用本地备用:', url);
+ self.log('JS资源加载失败: ' + url);
if (fallbackUrl) {
- ArgonResourceLoader.loadJS(fallbackUrl).then(resolve).catch(reject);
+ self.loadJS(fallbackUrl).then(resolve).catch(reject);
} else {
- reject(new Error('JS加载失败且无备用资源'));
+ var autoFallback = self.getFallbackUrl(url);
+ if (autoFallback) {
+ self.loadJS(autoFallback).then(resolve).catch(reject);
+ } else {
+ reject(new Error('JS加载失败且无备用资源'));
+ }
}
};
@@ -83,45 +189,45 @@
// 智能加载资源
smartLoad: function(url, type) {
- var fallbackUrl = null;
-
- // 查找备用资源
- for (var key in this.resourceMap) {
- if (url.indexOf(key) !== -1) {
- fallbackUrl = this.resourceMap[key];
- break;
- }
- }
-
if (type === 'css') {
- return this.loadCSS(url, fallbackUrl);
+ return this.loadCSS(url);
} else if (type === 'js') {
- return this.loadJS(url, fallbackUrl);
+ return this.loadJS(url);
+ } else {
+ return Promise.reject(new Error('不支持的资源类型: ' + type));
+ }
+ },
+
+ // 批量加载资源
+ loadMultiple: function(resources) {
+ var self = this;
+ var promises = resources.map(function(resource) {
+ return self.smartLoad(resource.url, resource.type);
+ });
+ return Promise.all(promises);
+ },
+
+ // 日志记录
+ log: function(message) {
+ if (this.config.enableLogging) {
+ if (typeof ArgonLogger !== 'undefined') {
+ ArgonLogger.info('[ResourceLoader] ' + message);
+ } else {
+ // 仅在管理员模式下输出到控制台
+ if (typeof window.argonUserRole !== 'undefined' && window.argonUserRole === 'administrator') {
+ console.log('[ArgonResourceLoader] ' + message);
+ }
+ }
}
}
};
- // 自动替换现有的资源加载
- var originalCreateElement = document.createElement;
- document.createElement = function(tagName) {
- var element = originalCreateElement.call(document, tagName);
-
- if (tagName.toLowerCase() === 'script') {
- var originalSetAttribute = element.setAttribute;
- element.setAttribute = function(name, value) {
- if (name === 'src' && value) {
- // 检查是否需要备用资源
- for (var key in ArgonResourceLoader.resourceMap) {
- if (value.indexOf(key) !== -1) {
- console.log('检测到外部JS资源,准备备用方案:', value);
- break;
- }
- }
- }
- return originalSetAttribute.call(this, name, value);
- };
- }
-
- return element;
- };
+ // 自动初始化
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', function() {
+ ArgonResourceLoader.init();
+ });
+ } else {
+ ArgonResourceLoader.init();
+ }
})();
\ No newline at end of file
diff --git a/assets/vendor/external/resource-monitor.js b/assets/vendor/external/resource-monitor.js
new file mode 100644
index 0000000..924fbfa
--- /dev/null
+++ b/assets/vendor/external/resource-monitor.js
@@ -0,0 +1,200 @@
+/* 外部资源监控系统 - 实时检测和自动切换 */
+(function() {
+ 'use strict';
+
+ window.ArgonResourceMonitor = {
+ // 监控配置
+ config: {
+ checkInterval: 30000, // 30秒检查一次
+ timeout: 5000, // 5秒超时
+ retryCount: 3, // 重试3次
+ enableLogging: true // 启用日志
+ },
+
+ // 资源状态
+ resourceStatus: {},
+
+ // 监控定时器
+ monitorTimer: null,
+
+ // 初始化监控
+ init: function() {
+ this.log('资源监控系统启动');
+ this.startMonitoring();
+ this.bindEvents();
+ },
+
+ // 开始监控
+ startMonitoring: function() {
+ var self = this;
+ this.monitorTimer = setInterval(function() {
+ self.checkAllResources();
+ }, this.config.checkInterval);
+ },
+
+ // 停止监控
+ stopMonitoring: function() {
+ if (this.monitorTimer) {
+ clearInterval(this.monitorTimer);
+ this.monitorTimer = null;
+ }
+ },
+
+ // 检查所有资源
+ checkAllResources: function() {
+ var self = this;
+ var resources = [
+ 'https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700',
+ 'https://static.geetest.com/v4/gt4.js',
+ 'https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js'
+ ];
+
+ resources.forEach(function(url) {
+ self.checkResource(url);
+ });
+ },
+
+ // 检查单个资源
+ checkResource: function(url) {
+ var self = this;
+ var startTime = Date.now();
+
+ // 创建测试请求
+ var xhr = new XMLHttpRequest();
+ xhr.timeout = this.config.timeout;
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4) {
+ var responseTime = Date.now() - startTime;
+ var status = xhr.status === 200 ? 'available' : 'unavailable';
+
+ self.updateResourceStatus(url, {
+ status: status,
+ responseTime: responseTime,
+ lastCheck: new Date().toISOString()
+ });
+ }
+ };
+
+ xhr.onerror = function() {
+ self.updateResourceStatus(url, {
+ status: 'unavailable',
+ responseTime: -1,
+ lastCheck: new Date().toISOString()
+ });
+ };
+
+ xhr.ontimeout = function() {
+ self.updateResourceStatus(url, {
+ status: 'timeout',
+ responseTime: self.config.timeout,
+ lastCheck: new Date().toISOString()
+ });
+ };
+
+ try {
+ xhr.open('HEAD', url, true);
+ xhr.send();
+ } catch (e) {
+ this.updateResourceStatus(url, {
+ status: 'error',
+ responseTime: -1,
+ lastCheck: new Date().toISOString(),
+ error: e.message
+ });
+ }
+ },
+
+ // 更新资源状态
+ updateResourceStatus: function(url, status) {
+ this.resourceStatus[url] = status;
+ this.log('资源状态更新: ' + url + ' - ' + status.status);
+
+ // 如果资源不可用,触发备用机制
+ if (status.status !== 'available') {
+ this.triggerFallback(url);
+ }
+ },
+
+ // 触发备用机制
+ triggerFallback: function(url) {
+ this.log('触发备用机制: ' + url);
+
+ // 发送自定义事件
+ var event = new CustomEvent('resourceUnavailable', {
+ detail: { url: url, status: this.resourceStatus[url] }
+ });
+ document.dispatchEvent(event);
+ },
+
+ // 绑定事件
+ bindEvents: function() {
+ var self = this;
+
+ // 监听页面可见性变化
+ document.addEventListener('visibilitychange', function() {
+ if (document.visibilityState === 'visible') {
+ self.checkAllResources();
+ }
+ });
+
+ // 监听网络状态变化
+ if ('onLine' in navigator) {
+ window.addEventListener('online', function() {
+ self.log('网络连接恢复,重新检查资源');
+ self.checkAllResources();
+ });
+
+ window.addEventListener('offline', function() {
+ self.log('网络连接断开');
+ });
+ }
+ },
+
+ // 获取资源状态报告
+ getStatusReport: function() {
+ return {
+ timestamp: new Date().toISOString(),
+ resources: this.resourceStatus,
+ summary: this.generateSummary()
+ };
+ },
+
+ // 生成状态摘要
+ generateSummary: function() {
+ var total = Object.keys(this.resourceStatus).length;
+ var available = 0;
+ var unavailable = 0;
+
+ for (var url in this.resourceStatus) {
+ if (this.resourceStatus[url].status === 'available') {
+ available++;
+ } else {
+ unavailable++;
+ }
+ }
+
+ return {
+ total: total,
+ available: available,
+ unavailable: unavailable,
+ availability: total > 0 ? (available / total * 100).toFixed(1) + '%' : '0%'
+ };
+ },
+
+ // 日志记录
+ log: function(message) {
+ if (this.config.enableLogging) {
+ console.log('[ArgonResourceMonitor] ' + message);
+ }
+ }
+ };
+
+ // 页面加载完成后自动启动监控
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', function() {
+ ArgonResourceMonitor.init();
+ });
+ } else {
+ ArgonResourceMonitor.init();
+ }
+})();
\ No newline at end of file
diff --git a/footer.php b/footer.php
index ab57c58..a35473f 100644
--- a/footer.php
+++ b/footer.php
@@ -190,7 +190,15 @@
-
+
@@ -232,17 +240,24 @@
-
+
-
+
-
+
-
+
+