{"id":97,"date":"2026-08-22T12:36:16","date_gmt":"2026-08-22T04:36:16","guid":{"rendered":"https:\/\/lucianism.top\/?page_id=97"},"modified":"2026-08-22T18:29:32","modified_gmt":"2026-08-22T10:29:32","slug":"calendar","status":"publish","type":"page","link":"https:\/\/lucianism.top\/index.php\/calendar","title":{"rendered":"\u65e5\u5386"},"content":{"rendered":"<div id=\"my-calendar-grid\"><\/div><script>\n    (function() {\n        var container = document.getElementById(\"my-calendar-grid\");\n        if (!container) return;\n\n        if (!window.myCalendar) {\n            window.myCalendar = {\n                notesCache: {},\n                canEdit: false,\n                nonce: \"50dcae4461\",\n                AJAX_URL: \"https:\/\/lucianism.top\/wp-admin\/admin-ajax.php\",\n                escapeHtml: function(text) {\n                    return String(text == null ? \"\" : text)\n                        .replace(\/&\/g, \"&amp;\")\n                        .replace(\/<\/g, \"&lt;\")\n                        .replace(\/>\/g, \"&gt;\")\n                        .replace(\/\"\/g, \"&quot;\");\n                },\n                inlineMd: function(text) {\n                    var esc = this.escapeHtml(text);\n                    var codes = [];\n                    esc = esc.replace(\/`([^`]+)`\/g, function(m, c) {\n                        codes.push(\"<code>\" + c + \"<\/code>\");\n                        return \"\\u0000\" + (codes.length - 1) + \"\\u0000\";\n                    });\n                    esc = esc.replace(\/\\[([^\\]]+)\\]\\(([^)\\s]+)\\)\/g, function(m, label, url) {\n                        var safe = \/^(https?:|mailto:|#|\\\/|\\.)\/i.test(url) ? url : \"#\";\n                        return \"<a href=\\\"\" + safe + \"\\\" target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">\" + label + \"<\/a>\";\n                    });\n                    esc = esc.replace(\/\\*\\*([^*]+)\\*\\*\/g, \"<strong>$1<\/strong>\");\n                    esc = esc.replace(\/~~([^~]+)~~\/g, \"<del>$1<\/del>\");\n                    esc = esc.replace(\/\\*([^*\\n]+)\\*\/g, \"<em>$1<\/em>\");\n                    esc = esc.replace(\/\\u0000(\\d+)\\u0000\/g, function(m, i) { return codes[parseInt(i, 10)]; });\n                    return esc;\n                },\n                renderMarkdown: function(src) {\n                    var text = String(src == null ? \"\" : src).replace(\/\\r\\n?\/g, \"\\n\");\n                    var lines = text.split(\"\\n\");\n                    var out = [];\n                    var inCode = false, codeBuf = [];\n                    var listType = null;\n                    var self = this;\n                    function closeList() {\n                        if (listType) { out.push(\"<\/\" + listType + \">\"); listType = null; }\n                    }\n                    for (var i = 0; i < lines.length; i++) {\n                        var line = lines[i];\n                        if (\/^\\s*```\/.test(line)) {\n                            if (inCode) {\n                                out.push(\"<pre><code>\" + self.escapeHtml(codeBuf.join(\"\\n\")) + \"<\/code><\/pre>\");\n                                codeBuf = [];\n                                inCode = false;\n                            } else {\n                                closeList();\n                                inCode = true;\n                            }\n                            continue;\n                        }\n                        if (inCode) { codeBuf.push(line); continue; }\n                        if (\/^\\s*$\/.test(line)) { closeList(); continue; }\n                        if (\/^\\s*([-*_])\\s*\\1\\s*\\1\\s*$\/.test(line)) {\n                            closeList();\n                            out.push(\"<hr>\");\n                            continue;\n                        }\n                        var h = line.match(\/^(#{1,6})\\s+(.*)$\/);\n                        if (h) {\n                            closeList();\n                            var lvl = h[1].length;\n                            out.push(\"<h\" + lvl + \">\" + self.inlineMd(h[2]) + \"<\/h\" + lvl + \">\");\n                            continue;\n                        }\n                        var bq = line.match(\/^>\\s?(.*)$\/);\n                        if (bq) {\n                            closeList();\n                            out.push(\"<blockquote>\" + self.inlineMd(bq[1]) + \"<\/blockquote>\");\n                            continue;\n                        }\n                        var ul = line.match(\/^\\s*[-*+]\\s+(.*)$\/);\n                        if (ul) {\n                            if (listType !== \"ul\") { closeList(); out.push(\"<ul>\"); listType = \"ul\"; }\n                            out.push(\"<li>\" + self.inlineMd(ul[1]) + \"<\/li>\");\n                            continue;\n                        }\n                        var ol = line.match(\/^\\s*\\d+[.)]\\s+(.*)$\/);\n                        if (ol) {\n                            if (listType !== \"ol\") { closeList(); out.push(\"<ol>\"); listType = \"ol\"; }\n                            out.push(\"<li>\" + self.inlineMd(ol[1]) + \"<\/li>\");\n                            continue;\n                        }\n                        closeList();\n                        out.push(\"<p>\" + self.inlineMd(line) + \"<\/p>\");\n                    }\n                    closeList();\n                    if (inCode) { out.push(\"<pre><code>\" + self.escapeHtml(codeBuf.join(\"\\n\")) + \"<\/code><\/pre>\"); }\n                    return out.join(\"\");\n                },\n                fetchNotes: async function() {\n                    try {\n                        var res = await fetch(this.AJAX_URL + \"?action=my_calendar_get_notes\");\n                        if (!res.ok) throw new Error(\"Failed\");\n                        var data = await res.json();\n                        return data.success ? data.data : {};\n                    } catch (e) {\n                        return {};\n                    }\n                },\n                updateNote: async function(date, title, content, type) {\n                    var fd = new FormData();\n                    fd.append(\"action\", \"my_calendar_update_note\");\n                    fd.append(\"nonce\", this.nonce);\n                    fd.append(\"date\", date);\n                    fd.append(\"title\", title);\n                    fd.append(\"content\", content);\n                    fd.append(\"type\", type);\n                    try {\n                        var res = await fetch(this.AJAX_URL, { method: \"POST\", body: fd });\n                        var data = await res.json();\n                        if (!data.success) {\n                            alert(\"\u4fdd\u5b58\u5931\u8d25\uff1a\" + (data.data || \"\u672a\u77e5\u9519\u8bef\"));\n                            return false;\n                        }\n                        return data.data.notes || {};\n                    } catch (e) {\n                        alert(\"\u4fdd\u5b58\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002\");\n                        return false;\n                    }\n                },\n                checkPermission: async function() {\n                    try {\n                        var res = await fetch(this.AJAX_URL + \"?action=my_calendar_check_perm\");\n                        if (!res.ok) throw new Error(\"Failed\");\n                        var data = await res.json();\n                        this.canEdit = !!(data.success && data.data && data.data.canEdit);\n                    } catch(e) {\n                        this.canEdit = false;\n                    }\n                },\n                importNotes: async function(jsonText) {\n                    var fd = new FormData();\n                    fd.append(\"action\", \"my_calendar_import\");\n                    fd.append(\"nonce\", this.nonce);\n                    fd.append(\"json\", jsonText);\n                    try {\n                        var res = await fetch(this.AJAX_URL, { method: \"POST\", body: fd });\n                        var data = await res.json();\n                        if (!data.success) {\n                            alert(\"\u5bfc\u5165\u5931\u8d25\uff1a\" + (data.data || \"\u672a\u77e5\u9519\u8bef\"));\n                            return false;\n                        }\n                        return data.data;\n                    } catch (e) {\n                        alert(\"\u5bfc\u5165\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002\");\n                        return false;\n                    }\n                },\n                init: async function() {\n                    this.notesCache = await this.fetchNotes();\n                    await this.checkPermission();\n                    document.dispatchEvent(new CustomEvent(\"my-calendar-ready\"));\n                    document.dispatchEvent(new CustomEvent(\"notes-updated\"));\n                }\n            };\n        }\n\n        if (!window.myCalendar._initialized) {\n            window.myCalendar._initialized = true;\n            window.myCalendar.init();\n        }\n\n        var shadow = container.attachShadow({ mode: \"open\" });\n\n        var style = document.createElement(\"style\");\n        style.textContent = `\n            :host { display: block; width: 100%; margin: 0 auto; font-family: consolas, monospace; }\n            .calendar { background: #ffffff; border-radius: 25px; padding: 20px 15px; box-shadow: 0 7px 29px 0 rgba(100,100,111,0.2); position: relative; }\n            .calendar-header { display: flex; justify-content: space-between; align-items: center; background: #9796f0; color: #fff; padding: 10px 15px; border-radius: 10px; font-weight: 700; }\n            .month-nav { display: flex; align-items: center; gap: 10px; }\n            .month-nav .month-change { cursor: pointer; font-size: 18px; user-select: none; padding: 0 6px; }\n            .month-nav .month-change:hover { background: rgba(255,255,255,0.3); border-radius: 50%; }\n            .month-picker { cursor: pointer; padding: 5px 10px; }\n            .year-picker { display: flex; align-items: center; gap: 8px; }\n            .year-change { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; background: transparent; color: #fff; margin: 0; font-size: 18px; user-select: none; }\n            .year-change:hover { background: rgba(255,255,255,0.3); transform: scale(1.1); }\n            .today-btn { display: inline-flex; align-items: center; justify-content: center; padding: 4px 14px; border-radius: 30px; background: rgba(255,255,255,0.25); color: #fff; font-size: 13px; font-weight: 600; cursor: pointer; transition: 0.2s; user-select: none; border: 1px solid rgba(255,255,255,0.2); margin-left: 5px; }\n            .today-btn:hover { background: rgba(255,255,255,0.5); transform: scale(1.05); }\n            .calendar-body { padding: 10px 0; }\n            .calendar-week-days { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; font-weight: 600; color: #686868; margin-bottom: 5px; }\n            .calendar-week-days div { padding: 8px 0; font-size: 14px; }\n            .calendar-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }\n            .calendar-days div {\n                display: flex;\n                align-items: center;\n                justify-content: center;\n                aspect-ratio: 1 \/ 1;\n                width: 100%;\n                font-size: 14px;\n                cursor: pointer;\n                border-radius: 10%;\n                position: relative;\n                background: #f9f9f9;\n                transition: background 0.2s;\n            }\n            .calendar-days div.other-month { color: #bbb; background: #f5f5f5; }\n            .calendar-days div.other-month:hover { background: #e0e0e0; }\n            .calendar-days div:hover { background: #fbc7d4; color: #000; }\n            .calendar-days div.current-date { background: #9796f0; color: #fff; }\n            .calendar-days div .note-dot { position: absolute; top: 2px; right: 2px; width: 8px; height: 8px; background: #ff6b6b; border-radius: 50%; pointer-events: none; }\n            .month-list { position: absolute; left: 20px; right: 20px; top: 80px; background: #ebebeb; display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; padding: 15px; border-radius: 20px; z-index: 999; box-shadow: 0 10px 30px rgba(0,0,0,0.2); visibility: hidden; pointer-events: none; }\n            .month-list.show { visibility: visible; pointer-events: auto; }\n            .month-list.hideonce { visibility: hidden; display: none; }\n            .month-list.hide { visibility: hidden; pointer-events: none; }\n            .month-list > div { text-align: center; padding: 8px; cursor: pointer; border-radius: 15px; }\n            .month-list > div:hover { background: #9796f0; color: #fff; }\n            .date-time-formate { display: flex; justify-content: space-around; align-items: center; margin-top: 20px; padding-top: 15px; border-top: 1px solid #eee; font-size: 14px; }\n            .day-text-formate { font-weight: bold; border-right: 3px solid #9796f0; padding-right: 15px; }\n            .date-time-value { display: flex; flex-direction: column; align-items: center; gap: 3px; }\n            .time-formate { font-size: 1.3rem; font-weight: 600; }\n            .tooltip { position: fixed; max-width: 260px; background: #333; color: #fff; padding: 10px 14px; border-radius: 12px; font-size: 14px; line-height: 1.5; box-shadow: 0 8px 25px rgba(0,0,0,0.3); pointer-events: none; z-index: 10001; opacity: 0; transition: opacity 0.15s ease; font-family: consolas, monospace; word-wrap: break-word; }\n            .tooltip.visible { opacity: 1; }\n            .tooltip .tt-title { font-weight: bold; color: #fbc7d4; margin-bottom: 4px; }\n            .tooltip .tt-content { color: #eee; }\n            \/* \u53f3\u952e\u83dc\u5355 *\/\n            .ctx-menu { position: fixed; z-index: 10010; background: #fff; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.25); min-width: 220px; padding: 6px; display: none; font-family: -apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif; }\n            .ctx-menu .ctx-title { padding: 6px 12px; font-size: 12px; color: #8b949e; font-weight: 600; border-bottom: 1px solid #eee; margin-bottom: 4px; font-family: ui-monospace, Consolas, monospace; }\n            .ctx-menu .ctx-item { padding: 8px 12px; font-size: 14px; border-radius: 8px; cursor: pointer; color: #24292f; }\n            .ctx-menu .ctx-item:hover { background: #f0f0f5; }\n            .ctx-menu .ctx-item.ctx-danger { color: #cf222e; }\n            .ctx-menu .ctx-item.ctx-danger:hover { background: #ffeef0; }\n            .ctx-menu .ctx-sep { height: 1px; background: #eee; margin: 4px 6px; }\n            \/* \u7f16\u8f91\u5f39\u7a97 *\/\n            .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 10020; display: none; align-items: center; justify-content: center; font-family: -apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif; }\n            .modal-overlay.show { display: flex; }\n            .modal { background: #fff; border-radius: 16px; width: 92%; max-width: 480px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); display: flex; flex-direction: column; max-height: 85vh; overflow: hidden; }\n            .modal-header { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; border-bottom: 1px solid #eee; }\n            .modal-header .m-title { font-weight: 600; font-size: 15px; color: #24292f; }\n            .modal-close { cursor: pointer; font-size: 22px; color: #8b949e; line-height: 1; padding: 0 4px; }\n            .modal-close:hover { color: #24292f; }\n            .modal-body { padding: 16px 18px; overflow-y: auto; }\n            .modal-body label { display: block; font-size: 13px; font-weight: 600; color: #57606a; margin: 10px 0 5px; }\n            .modal-body input[type=\"text\"], .modal-body textarea { width: 100%; padding: 8px 12px; border: 1px solid #d0d7de; border-radius: 10px; font-size: 14px; font-family: inherit; box-sizing: border-box; outline: none; transition: border 0.2s; }\n            .modal-body input[type=\"text\"]:focus, .modal-body textarea:focus { border-color: #9796f0; }\n            .modal-body textarea { resize: vertical; min-height: 110px; font-family: ui-monospace, Consolas, monospace; line-height: 1.5; }\n            .modal-hint { font-size: 12px; color: #8b949e; margin-top: 6px; }\n            .modal-preview-title { font-size: 12px; color: #8b949e; font-weight: 600; margin: 12px 0 6px; }\n            .modal-preview { border: 1px dashed #d0d7de; border-radius: 10px; padding: 10px 12px; background: #fafbfc; max-height: 180px; overflow-y: auto; }\n            .modal-footer { display: flex; align-items: center; gap: 8px; padding: 14px 18px; border-top: 1px solid #eee; }\n            .modal-footer button { padding: 8px 16px; border-radius: 30px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; transition: 0.2s; }\n            .modal-footer button:disabled { opacity: 0.6; cursor: not-allowed; }\n            .modal-footer .btn-primary { background: #9796f0; color: #fff; }\n            .modal-footer .btn-primary:hover { background: #7a78d6; }\n            .modal-footer .btn-ghost { background: #f0f0f5; color: #57606a; }\n            .modal-footer .btn-ghost:hover { background: #e2e2ea; }\n            .modal-footer .btn-danger { background: #ffeef0; color: #cf222e; }\n            .modal-footer .btn-danger:hover { background: #ffd7da; }\n            .modal-footer .spacer { flex: 1; }\n            \/* markdown \u6392\u7248\uff08\u9884\u89c8\u590d\u7528\uff09 *\/\n            .md-prose { line-height: 1.7; color: #24292f; word-break: break-word; font-size: 14px; }\n            .md-prose h1, .md-prose h2, .md-prose h3, .md-prose h4, .md-prose h5, .md-prose h6 { font-weight: 600; line-height: 1.3; margin: 0.7em 0 0.35em; }\n            .md-prose h1 { font-size: 3.0em; padding-bottom: 0.3em; border-bottom: 1px solid #eaecef; }\n            .md-prose h2 { font-size: 2.5em; padding-bottom: 0.3em; border-bottom: 1px solid #eaecef; }\n            .md-prose h3 { font-size: 1.1em; }\n            .md-prose h4 { font-size: 1em; }\n            .md-prose h5 { font-size: 0.9em; }\n            .md-prose h6 { font-size: 0.85em; color: #57606a; }\n            .md-prose p { margin: 0.4em 0; }\n            .md-prose ul, .md-prose ol { padding-left: 1.6em; margin: 0.4em 0; }\n            .md-prose li { margin: 0.2em 0; }\n            .md-prose code { background: rgba(175,184,193,0.25); padding: 0.15em 0.4em; border-radius: 6px; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.9em; }\n            .md-prose pre { background: #f6f8fa; padding: 12px 16px; border-radius: 8px; overflow-x: auto; margin: 0.5em 0; }\n            .md-prose pre code { background: none; padding: 0; }\n            .md-prose blockquote { border-left: 4px solid #d0d7de; color: #57606a; padding: 0 1em; margin: 0.5em 0; }\n            .md-prose a { color: #0969da; text-decoration: none; }\n            .md-prose a:hover { text-decoration: underline; }\n            .md-prose hr { border: none; border-top: 1px solid #eaecef; margin: 1em 0; }\n            .md-prose del { color: #57606a; }\n            .md-prose .md-empty { color: #8b949e; font-style: italic; margin: 0.2em 0; }\n            @media (max-width: 480px) {\n                .calendar { padding: 10px; }\n                .date-time-formate { flex-wrap: wrap; gap: 8px; }\n                .today-btn { font-size: 12px; padding: 3px 10px; }\n                .tooltip { max-width: 200px; font-size: 12px; }\n            }\n        `;\n\n        var wrapper = document.createElement(\"div\");\n        wrapper.className = \"calendar\";\n        wrapper.innerHTML = `\n            <div class=\"calendar-header\">\n                <div class=\"month-nav\">\n                    <span class=\"month-change\" id=\"prevMonth\">\u2039<\/span>\n                    <span class=\"month-picker\" id=\"mp_1\">May<\/span>\n                    <span class=\"month-change\" id=\"nextMonth\">\u203a<\/span>\n                <\/div>\n                <div class=\"year-picker\">\n    <span class=\"year-change\" id=\"pre_yr_1\">\u2039<\/span>\n    <span id=\"yr_1\" style=\"font-weight:bold;margin:0 5px;\">2020<\/span>\n    <span class=\"year-change\" id=\"next_yr_1\">\u203a<\/span>\n    <span class=\"today-btn\" id=\"todayBtn\">\u4eca\u65e5<\/span>\n    <span style=\"display:inline-flex;align-items:center;margin-left:8px;\">\n        <input type=\"number\" id=\"yearJumpInput\" placeholder=\"\u5e74\u4efd\"\n               style=\"width:60px;background:rgba(255,255,255,0.25);border:none;border-radius:6px;padding:4px 6px;color:#fff;font-size:13px;outline:none;text-align:center;caret-color:#fff;\">\n        <button id=\"yearJumpBtn\"\n                style=\"background:rgba(255,255,255,0.2);border:none;border-radius:6px;color:#fff;padding:4px 8px;cursor:pointer;font-size:13px;margin-left:4px;transition:0.2s;\">\n            \u8df3\n        <\/button>\n    <\/span>\n<\/div>\n            <\/div>\n            <div class=\"calendar-body\">\n                <div class=\"calendar-week-days\">\n                    <div>Sun<\/div><div>Mon<\/div><div>Tue<\/div>\n                    <div>Wed<\/div><div>Thu<\/div><div>Fri<\/div><div>Sat<\/div>\n                <\/div>\n                <div class=\"calendar-days\"><\/div>\n            <\/div>\n            <div class=\"date-time-formate\">\n                <div class=\"day-text-formate\">TODAY<\/div>\n                <div class=\"date-time-value\">\n                    <div class=\"time-formate\">00:00:00<\/div>\n                    <div class=\"date-formate\">January 1, 2020<\/div>\n                <\/div>\n            <\/div>\n            <div class=\"month-list hideonce\"><\/div>\n            <div class=\"tooltip\" id=\"tooltip\"><\/div>\n        `;\n\n        shadow.appendChild(style);\n        shadow.appendChild(wrapper);\n\n        var ctxMenuEl = document.createElement(\"div\");\n        ctxMenuEl.className = \"ctx-menu\";\n        ctxMenuEl.innerHTML = `\n            <div class=\"ctx-title\" id=\"ctxDate\"><\/div>\n            <div class=\"ctx-item\" id=\"ctxEditOnce\">\u270f\ufe0f \u7f16\u8f91\u300c\u4eca\u5e74\u4eca\u5929\u300d\u5907\u6ce8<\/div>\n            <div class=\"ctx-item\" id=\"ctxEditYearly\">\u270f\ufe0f \u7f16\u8f91\u300c\u6bcf\u5e74\u4eca\u65e5\u300d\u5907\u6ce8<\/div>\n            <div class=\"ctx-sep\"><\/div>\n            <div class=\"ctx-item ctx-danger\" id=\"ctxDelOnce\">\ud83d\uddd1\ufe0f \u5220\u9664\u300c\u4eca\u5e74\u4eca\u5929\u300d\u5907\u6ce8<\/div>\n            <div class=\"ctx-item ctx-danger\" id=\"ctxDelYearly\">\ud83d\uddd1\ufe0f \u5220\u9664\u300c\u6bcf\u5e74\u4eca\u65e5\u300d\u5907\u6ce8<\/div>\n        `;\n\n        var modalOverlay = document.createElement(\"div\");\n        modalOverlay.className = \"modal-overlay\";\n        modalOverlay.innerHTML = `\n            <div class=\"modal\">\n                <div class=\"modal-header\">\n                    <span class=\"m-title\" id=\"modalTitle\">\u7f16\u8f91\u5907\u6ce8<\/span>\n                    <span class=\"modal-close\" id=\"modalClose\">\u00d7<\/span>\n                <\/div>\n                <div class=\"modal-body\">\n                    <label>\u6807\u9898\uff08\u53ef\u9009\uff09<\/label>\n                    <input type=\"text\" id=\"modalTitleInput\" placeholder=\"\u8f93\u5165\u6807\u9898\">\n                    <label>\u5185\u5bb9\uff08\u652f\u6301 Markdown\uff09<\/label>\n                    <textarea id=\"modalContentInput\" placeholder=\"\u652f\u6301\uff1a**\u52a0\u7c97**\u3001*\u659c\u4f53*\u3001# \u6807\u9898\u3001- \u5217\u8868\u3001> \u5f15\u7528\"><\/textarea>\n                    <div class=\"modal-hint\">\u652f\u6301 Markdown\uff1a**\u52a0\u7c97** \u00b7 *\u659c\u4f53* \u00b7 # \u6807\u9898 \u00b7 - \u5217\u8868 \u00b7 > \u5f15\u7528 \u00b7 [\u94fe\u63a5](url) \u00b7 ~~\u5220\u9664\u7ebf~~<\/div>\n                    <div class=\"modal-preview-title\">\u5b9e\u65f6\u9884\u89c8<\/div>\n                    <div class=\"modal-preview md-prose\" id=\"modalPreview\"><\/div>\n                <\/div>\n                <div class=\"modal-footer\">\n                    <button class=\"btn-danger\" id=\"modalDelete\">\ud83d\uddd1\ufe0f \u5220\u9664<\/button>\n                    <span class=\"spacer\"><\/span>\n                    <button class=\"btn-ghost\" id=\"modalCancel\">\u53d6\u6d88<\/button>\n                    <button class=\"btn-primary\" id=\"modalSave\">\u4fdd\u5b58<\/button>\n                <\/div>\n            <\/div>\n        `;\n\n        shadow.appendChild(ctxMenuEl);\n        shadow.appendChild(modalOverlay);\n\n        var root = shadow;\n        var month_picker = root.querySelector(\"#mp_1\");\n        var yearSpan = root.querySelector(\"#yr_1\");\n        var preYearBtn = root.querySelector(\"#pre_yr_1\");\n        var nextYearBtn = root.querySelector(\"#next_yr_1\");\n        var prevMonthBtn = root.querySelector(\"#prevMonth\");\n        var nextMonthBtn = root.querySelector(\"#nextMonth\");\n        var todayBtn = root.querySelector(\"#todayBtn\");\n        var calendarDays = root.querySelector(\".calendar-days\");\n        var monthList = root.querySelector(\".month-list\");\n        var timeFormate = root.querySelector(\".time-formate\");\n        var dateFormate = root.querySelector(\".date-formate\");\n        var tooltip = root.querySelector(\"#tooltip\");\n\n        var ctxDate = ctxMenuEl.querySelector(\"#ctxDate\");\n        var ctxEditOnce = ctxMenuEl.querySelector(\"#ctxEditOnce\");\n        var ctxEditYearly = ctxMenuEl.querySelector(\"#ctxEditYearly\");\n        var ctxDelOnce = ctxMenuEl.querySelector(\"#ctxDelOnce\");\n        var ctxDelYearly = ctxMenuEl.querySelector(\"#ctxDelYearly\");\n\n        var modalTitle = modalOverlay.querySelector(\"#modalTitle\");\n        var modalClose = modalOverlay.querySelector(\"#modalClose\");\n        var modalTitleInput = modalOverlay.querySelector(\"#modalTitleInput\");\n        var modalContentInput = modalOverlay.querySelector(\"#modalContentInput\");\n        var modalPreview = modalOverlay.querySelector(\"#modalPreview\");\n        var modalDelete = modalOverlay.querySelector(\"#modalDelete\");\n        var modalCancel = modalOverlay.querySelector(\"#modalCancel\");\n        var modalSave = modalOverlay.querySelector(\"#modalSave\");\n\n        var month_names = [\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"];\n\n        var isLeapYear = function(y) { return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0); };\n        var getFebDays = function(y) { return isLeapYear(y) ? 29 : 28; };\n        var currentDate = new Date();\n        var currentMonth = currentDate.getMonth();\n        var currentYear = currentDate.getFullYear();\n\n        var ctx = null;\n        var modalCtx = null;\n\n        function hideCtxMenu() { ctxMenuEl.style.display = \"none\"; }\n\n        function buildCtxMenu() {\n            if (!ctx) return;\n            var notes = window.myCalendar.notesCache || {};\n            ctxDate.textContent = ctx.dateKey;\n            ctxEditOnce.style.display = \"\";\n            ctxEditYearly.style.display = \"\";\n            var onceNote = notes[ctx.dateKey];\n            var yearlyNote = notes[ctx.yearlyKey];\n            ctxDelOnce.style.display = (onceNote && (onceNote.title || onceNote.content)) ? \"\" : \"none\";\n            ctxDelYearly.style.display = (yearlyNote && (yearlyNote.title || yearlyNote.content)) ? \"\" : \"none\";\n        }\n\n        function showCtxMenu(x, y) {\n            buildCtxMenu();\n            ctxMenuEl.style.display = \"block\";\n            ctxMenuEl.style.left = \"0px\";\n            ctxMenuEl.style.top = \"0px\";\n            var r = ctxMenuEl.getBoundingClientRect();\n            var left = Math.max(8, Math.min(x, window.innerWidth - r.width - 8));\n            var top = Math.max(8, Math.min(y, window.innerHeight - r.height - 8));\n            ctxMenuEl.style.left = left + \"px\";\n            ctxMenuEl.style.top = top + \"px\";\n        }\n\n        function showModal() { modalOverlay.classList.add(\"show\"); }\n        function hideModal() { modalOverlay.classList.remove(\"show\"); }\n\n        function updatePreview() {\n            var title = modalTitleInput.value;\n            var content = modalContentInput.value;\n            var html = \"\";\n            if (title.trim()) {\n                html += \"<h1>\" + window.myCalendar.escapeHtml(title.trim()) + \"<\/h1>\";\n            }\n            if (content.trim()) {\n                html += window.myCalendar.renderMarkdown(content);\n            }\n            modalPreview.innerHTML = html || \"<p class=\\\"md-empty\\\">\u6682\u65e0\u5185\u5bb9<\/p>\";\n        }\n\n        function openModal(mode, key, y, m, d) {\n            modalCtx = { mode: mode, key: key, year: y, month: m, day: d };\n            var notes = window.myCalendar.notesCache || {};\n            var note = notes[key] || {};\n            modalTitle.textContent = (mode === \"yearly\" ? \"\ud83d\udd04 \u6bcf\u5e74\u4eca\u65e5\" : \"\ud83d\udccc \u4eca\u5e74\u4eca\u5929\") + \" \u00b7 \" + key;\n            modalTitleInput.value = note.title || \"\";\n            modalContentInput.value = note.content || \"\";\n            modalDelete.style.display = (note && (note.title || note.content)) ? \"\" : \"none\";\n            updatePreview();\n            showModal();\n            modalTitleInput.focus();\n        }\n\n        function refreshAfterSave(c) {\n            document.dispatchEvent(new CustomEvent(\"notes-updated\"));\n            document.dispatchEvent(new CustomEvent(\"calendar-date-selected\", {\n                detail: { year: c.year, month: c.month, day: c.day }\n            }));\n        }\n\n        ctxEditOnce.onclick = function() {\n            if (!ctx) return;\n            hideCtxMenu();\n            openModal(\"once\", ctx.dateKey, ctx.year, ctx.month, ctx.day);\n        };\n        ctxEditYearly.onclick = function() {\n            if (!ctx) return;\n            hideCtxMenu();\n            openModal(\"yearly\", ctx.yearlyKey, ctx.year, ctx.month, ctx.day);\n        };\n        ctxDelOnce.onclick = function() {\n            if (!ctx) return;\n            var c = ctx;\n            if (!confirm(\"\u786e\u5b9a\u5220\u9664\u300c\u4eca\u5e74\u4eca\u5929\u300d\u5907\u6ce8\uff08\" + c.dateKey + \"\uff09\u5417\uff1f\")) return;\n            hideCtxMenu();\n            window.myCalendar.updateNote(c.dateKey, \"\", \"\", \"once\").then(function(updated) {\n                if (updated !== false) {\n                    window.myCalendar.notesCache = updated;\n                    refreshAfterSave(c);\n                }\n            });\n        };\n        ctxDelYearly.onclick = function() {\n            if (!ctx) return;\n            var c = ctx;\n            if (!confirm(\"\u786e\u5b9a\u5220\u9664\u300c\u6bcf\u5e74\u4eca\u65e5\u300d\u5907\u6ce8\uff08\" + c.yearlyKey + \"\uff09\u5417\uff1f\")) return;\n            hideCtxMenu();\n            window.myCalendar.updateNote(c.yearlyKey, \"\", \"\", \"yearly\").then(function(updated) {\n                if (updated !== false) {\n                    window.myCalendar.notesCache = updated;\n                    refreshAfterSave(c);\n                }\n            });\n        };\n\n        modalSave.onclick = function() {\n            if (!modalCtx) return;\n            var title = modalTitleInput.value;\n            var content = modalContentInput.value;\n            modalSave.disabled = true;\n            modalSave.textContent = \"\u4fdd\u5b58\u4e2d...\";\n            window.myCalendar.updateNote(modalCtx.key, title, content, modalCtx.mode).then(function(updated) {\n                modalSave.disabled = false;\n                modalSave.textContent = \"\u4fdd\u5b58\";\n                if (updated !== false) {\n                    window.myCalendar.notesCache = updated;\n                    refreshAfterSave(modalCtx);\n                    hideModal();\n                }\n            });\n        };\n        modalDelete.onclick = function() {\n            if (!modalCtx) return;\n            var label = modalCtx.mode === \"yearly\" ? \"\u6bcf\u5e74\u4eca\u65e5\" : \"\u4eca\u5e74\u4eca\u5929\";\n            if (!confirm(\"\u786e\u5b9a\u5220\u9664\u300c\" + label + \"\u300d\u5907\u6ce8\uff08\" + modalCtx.key + \"\uff09\u5417\uff1f\")) return;\n            window.myCalendar.updateNote(modalCtx.key, \"\", \"\", modalCtx.mode).then(function(updated) {\n                if (updated !== false) {\n                    window.myCalendar.notesCache = updated;\n                    refreshAfterSave(modalCtx);\n                    hideModal();\n                }\n            });\n        };\n        modalCancel.onclick = hideModal;\n        modalClose.onclick = hideModal;\n        modalOverlay.addEventListener(\"click\", function(e) { if (e.target === modalOverlay) hideModal(); });\n        modalTitleInput.addEventListener(\"input\", updatePreview);\n        modalContentInput.addEventListener(\"input\", updatePreview);\n\n        document.addEventListener(\"click\", function(e) {\n            if (!ctxMenuEl.contains(e.target)) hideCtxMenu();\n        });\n        document.addEventListener(\"keydown\", function(e) {\n            if (e.key === \"Escape\") { hideCtxMenu(); hideModal(); }\n        });\n        window.addEventListener(\"scroll\", hideCtxMenu, true);\n        window.addEventListener(\"resize\", hideCtxMenu);\n\n        function generateCalendar(month, year) {\n            var notes = window.myCalendar.notesCache || {};\n            calendarDays.innerHTML = \"\";\n            var daysOfMonth = [31, getFebDays(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n            month_picker.innerHTML = month_names[month];\n            yearSpan.innerHTML = year;\n            var firstDay = new Date(year, month).getDay();\n            var totalDays = daysOfMonth[month];\n            var today = new Date();\n\n            var prevMonth = month === 0 ? 11 : month - 1;\n            var prevMonthYear = month === 0 ? year - 1 : year;\n            var prevMonthDays = daysOfMonth[prevMonth];\n            var nextMonth = month === 11 ? 0 : month + 1;\n            var nextMonthYear = month === 11 ? year + 1 : year;\n            var totalCells = Math.ceil((firstDay + totalDays) \/ 7) * 7;\n\n            for (var i = 0; i < totalCells; i++) {\n                var day = document.createElement(\"div\");\n                var dayNum, targetMonth, targetYear, isOther = false;\n\n                if (i < firstDay) {\n                    var offset = firstDay - i;\n                    dayNum = prevMonthDays - offset + 1;\n                    targetMonth = prevMonth;\n                    targetYear = prevMonthYear;\n                    isOther = true;\n                } else if (i >= firstDay + totalDays) {\n                    dayNum = i - (firstDay + totalDays) + 1;\n                    targetMonth = nextMonth;\n                    targetYear = nextMonthYear;\n                    isOther = true;\n                } else {\n                    dayNum = i - firstDay + 1;\n                    targetMonth = month;\n                    targetYear = year;\n                }\n\n                day.innerHTML = dayNum;\n                if (isOther) day.classList.add(\"other-month\");\n\n                if (!isOther && dayNum === today.getDate() && year === today.getFullYear() && month === today.getMonth()) {\n                    day.classList.add(\"current-date\");\n                }\n\n                var dateKey = targetYear + \"-\" + String(targetMonth+1).padStart(2,\"0\") + \"-\" + String(dayNum).padStart(2,\"0\");\n                var yearlyKey = String(targetMonth+1).padStart(2,\"0\") + \"-\" + String(dayNum).padStart(2,\"0\");\n                var hasNote = notes[dateKey] || notes[yearlyKey];\n                if (hasNote && (hasNote.title || hasNote.content)) {\n                    var dot = document.createElement(\"span\");\n                    dot.className = \"note-dot\";\n                    day.appendChild(dot);\n                }\n\n                (function(y, m, d, isOther) {\n                    day.addEventListener(\"mouseenter\", function(e) {\n                        var dateStr = y + \"-\" + String(m+1).padStart(2,\"0\") + \"-\" + String(d).padStart(2,\"0\");\n                        var yearlyKey2 = String(m+1).padStart(2,\"0\") + \"-\" + String(d).padStart(2,\"0\");\n                        var note = notes[dateStr] || notes[yearlyKey2] || null;\n                        if (note && (note.title || note.content)) {\n                            var html = \"\";\n                            if (note.title) html += \"<div class=\\\"tt-title\\\">\" + window.myCalendar.escapeHtml(note.title) + \"<\/div>\";\n                            if (note.content) html += \"<div class=\\\"tt-content\\\">\" + window.myCalendar.escapeHtml(note.content) + \"<\/div>\";\n                            tooltip.innerHTML = html;\n                            tooltip.style.left = (e.clientX + 15) + \"px\";\n                            tooltip.style.top = (e.clientY - 10) + \"px\";\n                            tooltip.classList.add(\"visible\");\n                        } else {\n                            tooltip.classList.remove(\"visible\");\n                        }\n                    });\n                    day.addEventListener(\"mouseleave\", function() { tooltip.classList.remove(\"visible\"); });\n                    day.addEventListener(\"mousemove\", function(e) {\n                        if (tooltip.classList.contains(\"visible\")) {\n                            tooltip.style.left = (e.clientX + 15) + \"px\";\n                            tooltip.style.top = (e.clientY - 10) + \"px\";\n                        }\n                    });\n                    day.addEventListener(\"click\", function() {\n                        if (isOther) {\n                            currentMonth = m;\n                            currentYear = y;\n                            generateCalendar(currentMonth, currentYear);\n                        }\n                        document.dispatchEvent(new CustomEvent(\"calendar-date-selected\", {\n                            detail: { year: y, month: m, day: d }\n                        }));\n                    });\n                    day.addEventListener(\"contextmenu\", function(e) {\n                        e.preventDefault();\n                        if (!window.myCalendar.canEdit) return;\n                        var dateKey2 = y + \"-\" + String(m+1).padStart(2,\"0\") + \"-\" + String(d).padStart(2,\"0\");\n                        var yearlyKey3 = String(m+1).padStart(2,\"0\") + \"-\" + String(d).padStart(2,\"0\");\n                        ctx = { year: y, month: m, day: d, dateKey: dateKey2, yearlyKey: yearlyKey3 };\n                        showCtxMenu(e.clientX, e.clientY);\n                    });\n                })(targetYear, targetMonth, dayNum, isOther);\n\n                calendarDays.appendChild(day);\n            }\n        }\n\n        function changeMonth(delta) {\n            var newMonth = currentMonth + delta;\n            var newYear = currentYear;\n            if (newMonth > 11) { newMonth = 0; newYear++; }\n            else if (newMonth < 0) { newMonth = 11; newYear--; }\n            currentMonth = newMonth;\n            currentYear = newYear;\n            generateCalendar(currentMonth, currentYear);\n            monthList.classList.remove(\"show\");\n            monthList.classList.add(\"hide\");\n        }\n\n        prevMonthBtn.addEventListener(\"click\", function() { changeMonth(-1); });\n        nextMonthBtn.addEventListener(\"click\", function() { changeMonth(1); });\n\n        todayBtn.addEventListener(\"click\", function() {\n            var now = new Date();\n            currentMonth = now.getMonth();\n            currentYear = now.getFullYear();\n            generateCalendar(currentMonth, currentYear);\n            monthList.classList.remove(\"show\");\n            monthList.classList.add(\"hide\");\n            document.dispatchEvent(new CustomEvent(\"calendar-date-selected\", {\n                detail: { year: now.getFullYear(), month: now.getMonth(), day: now.getDate() }\n            }));\n        });\n\n        preYearBtn.onclick = function() { currentYear--; generateCalendar(currentMonth, currentYear); };\n        nextYearBtn.onclick = function() { currentYear++; generateCalendar(currentMonth, currentYear); };\n\n        month_names.forEach(function(e, index) {\n            var monthDiv = document.createElement(\"div\");\n            monthDiv.innerHTML = \"<div>\" + e + \"<\/div>\";\n            monthList.append(monthDiv);\n            monthDiv.onclick = function() {\n                currentMonth = index;\n                generateCalendar(currentMonth, currentYear);\n                monthList.classList.remove(\"show\");\n                monthList.classList.add(\"hide\");\n            };\n        });\n\t\t\n\t\t\/\/ ========== \u6708\u4efd\u83dc\u5355\u5207\u6362\u529f\u80fd\uff08\u65b0\u589e\uff09 ==========\n\/\/ 1. \u70b9\u51fb\u6708\u4efd\u6587\u5b57\u5207\u6362\u83dc\u5355\nmonth_picker.addEventListener(\"click\", function(e) {\n    e.stopPropagation(); \/\/ \u9632\u6b62\u4e8b\u4ef6\u5192\u6ce1\u5230 document\n    if (monthList.classList.contains(\"show\")) {\n        \/\/ \u5df2\u6253\u5f00 \u2192 \u5173\u95ed\n        monthList.classList.remove(\"show\");\n        monthList.classList.add(\"hide\");\n    } else {\n        \/\/ \u5df2\u5173\u95ed \u2192 \u6253\u5f00\n        monthList.classList.remove(\"hideonce\", \"hide\");\n        monthList.classList.add(\"show\");\n    }\n});\n\n\/\/ 2. \u70b9\u51fb\u65e5\u5386\u5916\u90e8\u4efb\u610f\u4f4d\u7f6e\u81ea\u52a8\u5173\u95ed\u83dc\u5355\ndocument.addEventListener(\"click\", function(e) {\n    \/\/ \u5982\u679c\u70b9\u51fb\u53d1\u751f\u5728\u65e5\u5386\u5185\u90e8\uff08shadow DOM \u5185\uff09\uff0c\u5219\u4e0d\u5904\u7406\n    if (root.contains(e.target)) return;\n    \/\/ \u5982\u679c\u83dc\u5355\u662f\u6253\u5f00\u72b6\u6001\uff0c\u5219\u5173\u95ed\n    if (monthList.classList.contains(\"show\")) {\n        monthList.classList.remove(\"show\");\n        monthList.classList.add(\"hide\");\n    }\n});\n\/\/ =============================================\n\n        \/\/ \u5e74\u4efd\u8df3\u8f6c\u529f\u80fd\nvar yearJumpInput = root.querySelector(\"#yearJumpInput\");\nvar yearJumpBtn = root.querySelector(\"#yearJumpBtn\");\n\nfunction jumpToYear() {\n    var val = parseInt(yearJumpInput.value, 10);\n    if (!isNaN(val) && val > 0) {\n        currentYear = val;\n        generateCalendar(currentMonth, currentYear);\n        yearJumpInput.value = currentYear;\n    } else {\n        yearJumpInput.value = currentYear;\n    }\n}\n\nyearJumpBtn.addEventListener(\"click\", jumpToYear);\nyearJumpInput.addEventListener(\"keydown\", function(e) {\n    if (e.key === \"Enter\") {\n        e.preventDefault();\n        jumpToYear();\n    }\n});\n\/\/ \u53ef\u9009\uff1a\u5931\u53bb\u7126\u70b9\u65f6\u81ea\u52a8\u8df3\u8f6c\uff08\u5982\u4e0d\u9700\u8981\u53ef\u5220\u9664\u6b64\u76d1\u542c\uff09\nyearJumpInput.addEventListener(\"blur\", function() {\n    var val = parseInt(this.value, 10);\n    if (!isNaN(val) && val > 0 && val !== currentYear) {\n        currentYear = val;\n        generateCalendar(currentMonth, currentYear);\n        this.value = currentYear;\n    } else {\n        this.value = currentYear;\n    }\n});\n\n        document.addEventListener(\"notes-updated\", function() {\n            generateCalendar(currentMonth, currentYear);\n        });\n\n        function initCalendar() {\n            generateCalendar(currentMonth, currentYear);\n            if (window.myCalendar && window.myCalendar._initialized) {\n                var now = new Date();\n                document.dispatchEvent(new CustomEvent(\"calendar-date-selected\", {\n                    detail: { year: now.getFullYear(), month: now.getMonth(), day: now.getDate() }\n                }));\n            } else {\n                document.addEventListener(\"my-calendar-ready\", function() {\n                    var now = new Date();\n                    document.dispatchEvent(new CustomEvent(\"calendar-date-selected\", {\n                        detail: { year: now.getFullYear(), month: now.getMonth(), day: now.getDate() }\n                    }));\n                });\n            }\n        }\n\n        var updateDateTime = function() {\n            var now = new Date();\n            var dateOpt = { year: \"numeric\", month: \"long\", day: \"numeric\", weekday: \"long\" };\n            dateFormate.textContent = new Intl.DateTimeFormat(\"en-US\", dateOpt).format(now);\n            var timeOpt = { hour: \"2-digit\", minute: \"2-digit\", second: \"2-digit\" };\n            timeFormate.textContent = new Intl.DateTimeFormat(\"en-US\", timeOpt).format(now);\n        };\n        updateDateTime();\n        setInterval(updateDateTime, 1000);\n\n        initCalendar();\n    })();\n    <\/script>\n\n\n<div id=\"my-calendar-info\"><\/div><script>\n    (function() {\n        var container = document.getElementById(\"my-calendar-info\");\n        if (!container) return;\n\n        if (!window.myCalendar) {\n            window.myCalendar = {\n                notesCache: {},\n                canEdit: false,\n                nonce: \"50dcae4461\",\n                AJAX_URL: \"https:\/\/lucianism.top\/wp-admin\/admin-ajax.php\",\n                escapeHtml: function(text) {\n                    return String(text == null ? \"\" : text)\n                        .replace(\/&\/g, \"&amp;\")\n                        .replace(\/<\/g, \"&lt;\")\n                        .replace(\/>\/g, \"&gt;\")\n                        .replace(\/\"\/g, \"&quot;\");\n                },\n                inlineMd: function(text) {\n                    var esc = this.escapeHtml(text);\n                    var codes = [];\n                    esc = esc.replace(\/`([^`]+)`\/g, function(m, c) {\n                        codes.push(\"<code>\" + c + \"<\/code>\");\n                        return \"\\u0000\" + (codes.length - 1) + \"\\u0000\";\n                    });\n                    esc = esc.replace(\/\\[([^\\]]+)\\]\\(([^)\\s]+)\\)\/g, function(m, label, url) {\n                        var safe = \/^(https?:|mailto:|#|\\\/|\\.)\/i.test(url) ? url : \"#\";\n                        return \"<a href=\\\"\" + safe + \"\\\" target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">\" + label + \"<\/a>\";\n                    });\n                    esc = esc.replace(\/\\*\\*([^*]+)\\*\\*\/g, \"<strong>$1<\/strong>\");\n                    esc = esc.replace(\/~~([^~]+)~~\/g, \"<del>$1<\/del>\");\n                    esc = esc.replace(\/\\*([^*\\n]+)\\*\/g, \"<em>$1<\/em>\");\n                    esc = esc.replace(\/\\u0000(\\d+)\\u0000\/g, function(m, i) { return codes[parseInt(i, 10)]; });\n                    return esc;\n                },\n                renderMarkdown: function(src) {\n                    var text = String(src == null ? \"\" : src).replace(\/\\r\\n?\/g, \"\\n\");\n                    var lines = text.split(\"\\n\");\n                    var out = [];\n                    var inCode = false, codeBuf = [];\n                    var listType = null;\n                    var self = this;\n                    function closeList() {\n                        if (listType) { out.push(\"<\/\" + listType + \">\"); listType = null; }\n                    }\n                    for (var i = 0; i < lines.length; i++) {\n                        var line = lines[i];\n                        if (\/^\\s*```\/.test(line)) {\n                            if (inCode) {\n                                out.push(\"<pre><code>\" + self.escapeHtml(codeBuf.join(\"\\n\")) + \"<\/code><\/pre>\");\n                                codeBuf = [];\n                                inCode = false;\n                            } else {\n                                closeList();\n                                inCode = true;\n                            }\n                            continue;\n                        }\n                        if (inCode) { codeBuf.push(line); continue; }\n                        if (\/^\\s*$\/.test(line)) { closeList(); continue; }\n                        if (\/^\\s*([-*_])\\s*\\1\\s*\\1\\s*$\/.test(line)) {\n                            closeList();\n                            out.push(\"<hr>\");\n                            continue;\n                        }\n                        var h = line.match(\/^(#{1,6})\\s+(.*)$\/);\n                        if (h) {\n                            closeList();\n                            var lvl = h[1].length;\n                            out.push(\"<h\" + lvl + \">\" + self.inlineMd(h[2]) + \"<\/h\" + lvl + \">\");\n                            continue;\n                        }\n                        var bq = line.match(\/^>\\s?(.*)$\/);\n                        if (bq) {\n                            closeList();\n                            out.push(\"<blockquote>\" + self.inlineMd(bq[1]) + \"<\/blockquote>\");\n                            continue;\n                        }\n                        var ul = line.match(\/^\\s*[-*+]\\s+(.*)$\/);\n                        if (ul) {\n                            if (listType !== \"ul\") { closeList(); out.push(\"<ul>\"); listType = \"ul\"; }\n                            out.push(\"<li>\" + self.inlineMd(ul[1]) + \"<\/li>\");\n                            continue;\n                        }\n                        var ol = line.match(\/^\\s*\\d+[.)]\\s+(.*)$\/);\n                        if (ol) {\n                            if (listType !== \"ol\") { closeList(); out.push(\"<ol>\"); listType = \"ol\"; }\n                            out.push(\"<li>\" + self.inlineMd(ol[1]) + \"<\/li>\");\n                            continue;\n                        }\n                        closeList();\n                        out.push(\"<p>\" + self.inlineMd(line) + \"<\/p>\");\n                    }\n                    closeList();\n                    if (inCode) { out.push(\"<pre><code>\" + self.escapeHtml(codeBuf.join(\"\\n\")) + \"<\/code><\/pre>\"); }\n                    return out.join(\"\");\n                },\n                fetchNotes: async function() {\n                    try {\n                        var res = await fetch(this.AJAX_URL + \"?action=my_calendar_get_notes\");\n                        if (!res.ok) throw new Error(\"Failed\");\n                        var data = await res.json();\n                        return data.success ? data.data : {};\n                    } catch (e) {\n                        return {};\n                    }\n                },\n                updateNote: async function(date, title, content, type) {\n                    var fd = new FormData();\n                    fd.append(\"action\", \"my_calendar_update_note\");\n                    fd.append(\"nonce\", this.nonce);\n                    fd.append(\"date\", date);\n                    fd.append(\"title\", title);\n                    fd.append(\"content\", content);\n                    fd.append(\"type\", type);\n                    try {\n                        var res = await fetch(this.AJAX_URL, { method: \"POST\", body: fd });\n                        var data = await res.json();\n                        if (!data.success) {\n                            alert(\"\u4fdd\u5b58\u5931\u8d25\uff1a\" + (data.data || \"\u672a\u77e5\u9519\u8bef\"));\n                            return false;\n                        }\n                        return data.data.notes || {};\n                    } catch (e) {\n                        alert(\"\u4fdd\u5b58\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002\");\n                        return false;\n                    }\n                },\n                checkPermission: async function() {\n                    try {\n                        var res = await fetch(this.AJAX_URL + \"?action=my_calendar_check_perm\");\n                        if (!res.ok) throw new Error(\"Failed\");\n                        var data = await res.json();\n                        this.canEdit = !!(data.success && data.data && data.data.canEdit);\n                    } catch(e) {\n                        this.canEdit = false;\n                    }\n                },\n                importNotes: async function(jsonText) {\n                    var fd = new FormData();\n                    fd.append(\"action\", \"my_calendar_import\");\n                    fd.append(\"nonce\", this.nonce);\n                    fd.append(\"json\", jsonText);\n                    try {\n                        var res = await fetch(this.AJAX_URL, { method: \"POST\", body: fd });\n                        var data = await res.json();\n                        if (!data.success) {\n                            alert(\"\u5bfc\u5165\u5931\u8d25\uff1a\" + (data.data || \"\u672a\u77e5\u9519\u8bef\"));\n                            return false;\n                        }\n                        return data.data;\n                    } catch (e) {\n                        alert(\"\u5bfc\u5165\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002\");\n                        return false;\n                    }\n                },\n                init: async function() {\n                    this.notesCache = await this.fetchNotes();\n                    await this.checkPermission();\n                    document.dispatchEvent(new CustomEvent(\"my-calendar-ready\"));\n                    document.dispatchEvent(new CustomEvent(\"notes-updated\"));\n                }\n            };\n        }\n\n        if (!window.myCalendar._initialized) {\n            window.myCalendar._initialized = true;\n            window.myCalendar.init();\n        }\n\n        var shadow = container.attachShadow({ mode: \"open\" });\n\n        var style = document.createElement(\"style\");\n        style.textContent = `\n            :host { display: block; width: 100%; margin: 0 auto; font-family: -apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif; }\n            .md-note {\n                background: #ffffff;\n                border: 1px solid #eaecef;\n                border-radius: 12px;\n                padding: 20px 24px;\n                box-shadow: 0 1px 3px rgba(0,0,0,0.04);\n                margin-top: 20px;\n            }\n            .md-header {\n                display: flex;\n                justify-content: space-between;\n                align-items: baseline;\n                padding-bottom: 10px;\n                border-bottom: 1px solid #eaecef;\n                margin-bottom: 6px;\n            }\n            .md-title { font-size: 18px; font-weight: 600; margin: 0; color: #24292f; }\n            .md-date { font-size: 13px; color: #8b949e; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }\n            .md-header-right { display: flex; align-items: baseline; gap: 10px; }\n            .md-import-btn { padding: 4px 12px; border: 1px solid #d0d7de; border-radius: 20px; background: #f6f8fa; color: #57606a; font-size: 12px; font-weight: 600; cursor: pointer; transition: 0.2s; }\n            .md-import-btn:hover { background: #9796f0; color: #fff; border-color: #9796f0; }\n            .imp-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 10020; display: none; align-items: center; justify-content: center; font-family: -apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif; }\n            .imp-overlay.show { display: flex; }\n            .imp-modal { background: #fff; border-radius: 16px; width: 92%; max-width: 440px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); display: flex; flex-direction: column; max-height: 80vh; overflow: hidden; }\n            .imp-header { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; border-bottom: 1px solid #eee; }\n            .imp-header .imp-title { font-weight: 600; font-size: 15px; color: #24292f; }\n            .imp-close { cursor: pointer; font-size: 22px; color: #8b949e; line-height: 1; padding: 0 4px; }\n            .imp-close:hover { color: #24292f; }\n            .imp-body { padding: 16px 18px; overflow-y: auto; }\n            .imp-summary { font-size: 15px; color: #24292f; line-height: 1.6; }\n            .imp-summary .ok { color: #1a7f37; font-weight: 600; }\n            .imp-summary .warn { color: #bf8700; font-weight: 600; }\n            .imp-errors { margin-top: 12px; padding: 10px 12px; background: #fff8f0; border: 1px solid #ffe2c2; border-radius: 10px; font-size: 12px; color: #9a6700; line-height: 1.7; max-height: 180px; overflow-y: auto; white-space: pre-wrap; word-break: break-word; }\n            .imp-footer { display: flex; justify-content: flex-end; padding: 12px 18px; border-top: 1px solid #eee; }\n            .imp-footer button { padding: 8px 20px; border-radius: 30px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; background: #9796f0; color: #fff; transition: 0.2s; }\n            .imp-footer button:hover { background: #7a78d6; }\n            .md-section { padding: 12px 0 4px; }\n            .md-section-title { font-size: 18px; font-weight: 600; color: #57606a; margin: 0 0 8px; }\n            .md-divider { border: none; border-top: 1px solid #eaecef; margin: 0; }\n            .md-prose { line-height: 1.7; color: #24292f; word-break: break-word; font-size: 14px; }\n            .md-prose h1, .md-prose h2, .md-prose h3, .md-prose h4, .md-prose h5, .md-prose h6 { font-weight: 600; line-height: 1.3; margin: 0.7em 0 0.35em; }\n            .md-prose h1 { font-size: 1.4em; padding-bottom: 0.3em; border-bottom: 1px solid #eaecef; }\n            .md-prose h2 { font-size: 1.25em; padding-bottom: 0.3em; border-bottom: 1px solid #eaecef; }\n            .md-prose h3 { font-size: 1.1em; }\n            .md-prose h4 { font-size: 1em; }\n            .md-prose h5 { font-size: 0.9em; }\n            .md-prose h6 { font-size: 0.85em; color: #57606a; }\n            .md-prose p { margin: 0.4em 0; }\n            .md-prose ul, .md-prose ol { padding-left: 1.6em; margin: 0.4em 0; }\n            .md-prose li { margin: 0.2em 0; }\n            .md-prose code { background: rgba(175,184,193,0.25); padding: 0.15em 0.4em; border-radius: 6px; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.9em; }\n            .md-prose pre { background: #f6f8fa; padding: 12px 16px; border-radius: 8px; overflow-x: auto; margin: 0.5em 0; }\n            .md-prose pre code { background: none; padding: 0; }\n            .md-prose blockquote { border-left: 4px solid #d0d7de; color: #57606a; padding: 0 1em; margin: 0.5em 0; }\n            .md-prose a { color: #0969da; text-decoration: none; }\n            .md-prose a:hover { text-decoration: underline; }\n            .md-prose hr { border: none; border-top: 1px solid #eaecef; margin: 1em 0; }\n            .md-prose del { color: #57606a; }\n            .md-prose .md-empty { color: #8b949e; font-style: italic; margin: 0.2em 0; }\n            @media (max-width: 480px) {\n                .md-note { padding: 12px 16px; }\n            }\n        `;\n\n        var wrapper = document.createElement(\"div\");\n        wrapper.className = \"md-note\";\n        wrapper.innerHTML = `\n            <header class=\"md-header\">\n                <h1 class=\"md-title\">\ud83d\udcc5 \u5f53\u65e5\u4fe1\u606f<\/h1>\n                <div class=\"md-header-right\">\n                    <span class=\"md-date\" id=\"selectedDateDisplay\">\u672a\u9009\u62e9\u65e5\u671f<\/span>\n                    <button class=\"md-import-btn\" id=\"importBtn\" style=\"display:none;\">\ud83d\udce5 \u5bfc\u5165 JSON<\/button>\n                <\/div>\n            <\/header>\n            <section class=\"md-section\">\n                <h2 class=\"md-section-title\">\ud83d\udd04 \u6bcf\u5e74\u4eca\u65e5<\/h2>\n                <div class=\"md-prose\" id=\"yearlyContent\"><p class=\"md-empty\">\u70b9\u51fb\u65e5\u671f\u67e5\u770b\u5e74\u5ea6\u5907\u6ce8<\/p><\/div>\n            <\/section>\n            <hr class=\"md-divider\">\n            <section class=\"md-section\">\n                <h2 class=\"md-section-title\">\ud83d\udccc \u4eca\u5e74\u4eca\u5929<\/h2>\n                <div class=\"md-prose\" id=\"onceContent\"><p class=\"md-empty\">\u70b9\u51fb\u65e5\u671f\u67e5\u770b\u4eca\u5e74\u5907\u6ce8<\/p><\/div>\n            <\/section>\n        `;\n\n        shadow.appendChild(style);\n        shadow.appendChild(wrapper);\n\n        var root = shadow;\n        var selectedDateDisplay = root.querySelector(\"#selectedDateDisplay\");\n        var yearlyContent = root.querySelector(\"#yearlyContent\");\n        var onceContent = root.querySelector(\"#onceContent\");\n        var importBtn = root.querySelector(\"#importBtn\");\n\n        var importFile = document.createElement(\"input\");\n        importFile.type = \"file\";\n        importFile.accept = \".json,application\/json\";\n        importFile.style.display = \"none\";\n        shadow.appendChild(importFile);\n\n        var resultOverlay = document.createElement(\"div\");\n        resultOverlay.className = \"imp-overlay\";\n        resultOverlay.innerHTML = `\n            <div class=\"imp-modal\">\n                <div class=\"imp-header\">\n                    <span class=\"imp-title\">\u5bfc\u5165\u7ed3\u679c<\/span>\n                    <span class=\"imp-close\" id=\"importClose\">\u00d7<\/span>\n                <\/div>\n                <div class=\"imp-body\">\n                    <div class=\"imp-summary\" id=\"importSummary\"><\/div>\n                    <div class=\"imp-errors\" id=\"importErrors\" style=\"display:none;\"><\/div>\n                <\/div>\n                <div class=\"imp-footer\">\n                    <button id=\"importOk\">\u786e\u5b9a<\/button>\n                <\/div>\n            <\/div>\n        `;\n        shadow.appendChild(resultOverlay);\n\n        var importClose = resultOverlay.querySelector(\"#importClose\");\n        var importSummary = resultOverlay.querySelector(\"#importSummary\");\n        var importErrors = resultOverlay.querySelector(\"#importErrors\");\n        var importOk = resultOverlay.querySelector(\"#importOk\");\n\n        function renderMarkdownNote(el, note, label) {\n            if (note && (note.title || note.content)) {\n                var html = \"\";\n                if (note.title) {\n                    html += \"<h1>\" + window.myCalendar.escapeHtml(note.title) + \"<\/h1>\";\n                }\n                if (note.content) {\n                    html += window.myCalendar.renderMarkdown(note.content);\n                }\n                el.innerHTML = html;\n            } else {\n                el.innerHTML = \"<p class=\\\"md-empty\\\">\u6682\u65e0\" + label + \"\u5907\u6ce8<\/p>\";\n            }\n        }\n\n        function updateInfoPanel(year, month, day) {\n            lastSel = { year: year, month: month, day: day };\n            importBtn.style.display = window.myCalendar.canEdit ? \"\" : \"none\";\n            var notes = window.myCalendar.notesCache || {};\n            var dateStr = year + \"-\" + String(month+1).padStart(2,\"0\") + \"-\" + String(day).padStart(2,\"0\");\n            var yearlyKey = String(month+1).padStart(2,\"0\") + \"-\" + String(day).padStart(2,\"0\");\n            selectedDateDisplay.textContent = dateStr;\n\n            var yearlyNote = notes[yearlyKey] || null;\n            var onceNote = notes[dateStr] || null;\n\n            renderMarkdownNote(yearlyContent, yearlyNote, \"\u5e74\u5ea6\");\n            renderMarkdownNote(onceContent, onceNote, \"\u4eca\u5e74\");\n        }\n\n        var lastSel = null;\n\n        importBtn.onclick = function() { importFile.click(); };\n\n        importFile.addEventListener(\"change\", function(e) {\n            var file = e.target.files[0];\n            importFile.value = \"\";\n            if (!file) return;\n            var reader = new FileReader();\n            reader.onload = function(ev) {\n                var text = String(ev.target.result);\n                var parsed;\n                try {\n                    parsed = JSON.parse(text);\n                } catch (err) {\n                    alert(\"JSON \u89e3\u6790\u5931\u8d25\uff1a\" + err.message);\n                    return;\n                }\n                if (!parsed || typeof parsed !== \"object\" || !Array.isArray(parsed.notes)) {\n                    alert(\"JSON \u7ed3\u6784\u65e0\u6548\uff0c\u5e94\u4e3a { \\\"notes\\\": [...] } \u683c\u5f0f\");\n                    return;\n                }\n                var currentCount = Object.keys(window.myCalendar.notesCache || {}).length;\n                if (!confirm(\"\u5bfc\u5165\u5c06\u3010\u66ff\u6362\u3011\u5f53\u524d\u5168\u90e8 \" + currentCount + \" \u6761\u5907\u6ce8\uff0c\u786e\u5b9a\u7ee7\u7eed\uff1f\")) return;\n                window.myCalendar.importNotes(text).then(function(data) {\n                    if (data !== false) {\n                        window.myCalendar.notesCache = data.notes || {};\n                        document.dispatchEvent(new CustomEvent(\"notes-updated\"));\n                        showImportResult(data);\n                    }\n                });\n            };\n            reader.onerror = function() { alert(\"\u6587\u4ef6\u8bfb\u53d6\u5931\u8d25\"); };\n            reader.readAsText(file);\n        });\n\n        function showImportResult(data) {\n            var imported = data.imported || 0;\n            var skipped = data.skipped || 0;\n            var errors = data.errors || [];\n            var sum = \"\u6210\u529f\u5bfc\u5165 <span class=\\\"ok\\\">\" + imported + \"<\/span> \u6761\";\n            if (skipped > 0) {\n                sum += \"\uff0c\u8df3\u8fc7 <span class=\\\"warn\\\">\" + skipped + \"<\/span> \u6761\";\n            }\n            importSummary.innerHTML = sum;\n            if (errors.length > 0) {\n                importErrors.textContent = errors.join(\"\\n\");\n                importErrors.style.display = \"\";\n            } else {\n                importErrors.style.display = \"none\";\n            }\n            resultOverlay.classList.add(\"show\");\n        }\n\n        importClose.onclick = function() { resultOverlay.classList.remove(\"show\"); };\n        importOk.onclick = function() { resultOverlay.classList.remove(\"show\"); };\n        resultOverlay.addEventListener(\"click\", function(e) { if (e.target === resultOverlay) resultOverlay.classList.remove(\"show\"); });\n\n        document.addEventListener(\"notes-updated\", function() {\n            if (lastSel) {\n                updateInfoPanel(lastSel.year, lastSel.month, lastSel.day);\n            }\n        });\n\n        document.addEventListener(\"calendar-date-selected\", function(e) {\n            var detail = e.detail;\n            if (detail && detail.year !== undefined) {\n                updateInfoPanel(detail.year, detail.month, detail.day);\n            }\n        });\n\n        if (window.myCalendar && window.myCalendar._initialized) {\n            var now = new Date();\n            updateInfoPanel(now.getFullYear(), now.getMonth(), now.getDate());\n        } else {\n            document.addEventListener(\"my-calendar-ready\", function() {\n                var now = new Date();\n                updateInfoPanel(now.getFullYear(), now.getMonth(), now.getDate());\n            });\n        }\n    })();\n    <\/script>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-97","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/pages\/97","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/comments?post=97"}],"version-history":[{"count":11,"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/pages\/97\/revisions"}],"predecessor-version":[{"id":115,"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/pages\/97\/revisions\/115"}],"wp:attachment":[{"href":"https:\/\/lucianism.top\/index.php\/wp-json\/wp\/v2\/media?parent=97"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}