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 = + ''; + 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 @@ - + - + - + - + +