|
11 | 11 | /> |
12 | 12 | <!--主题--> |
13 | 13 | <link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css" /> |
| 14 | + <!-- 你也可以在这里添加自定义 CSS 来控制侧边栏折叠样式,或者使用 JS 动态添加 (如下面的插件所示) --> |
| 15 | + <style> |
| 16 | + /* CSS for Collapsible Sidebar */ |
| 17 | + /* Hide nested lists by default */ |
| 18 | + .sidebar .sidebar-nav ul { |
| 19 | + display: none; |
| 20 | + } |
| 21 | + /* Show nested lists when the parent LI has the 'expanded' class */ |
| 22 | + .sidebar .sidebar-nav li.collapsible.expanded > ul { |
| 23 | + display: block; |
| 24 | + } |
| 25 | + /* Optional: Style for collapsible items/links */ |
| 26 | + .sidebar .sidebar-nav li.collapsible > a { |
| 27 | + /* Needed if you add content like arrows */ |
| 28 | + position: relative; /* Allow positioning of pseudo-elements */ |
| 29 | + padding-right: 20px; /* Make space for the indicator */ |
| 30 | + cursor: pointer; /* Indicate it's clickable */ |
| 31 | + } |
| 32 | + /* Optional: Add expand/collapse indicator (right arrow for collapsed, down arrow for expanded) */ |
| 33 | + .sidebar .sidebar-nav li.collapsible > a::before { |
| 34 | + content: '►'; /* Right arrow when collapsed */ |
| 35 | + position: absolute; |
| 36 | + right: 5px; /* Position relative to the link */ |
| 37 | + top: 50%; |
| 38 | + transform: translateY(-50%); |
| 39 | + font-size: 0.8em; |
| 40 | + transition: transform 0.2s ease-in-out; /* Smooth transition for indicator */ |
| 41 | + } |
| 42 | + .sidebar .sidebar-nav li.collapsible.expanded > a::before { |
| 43 | + content: '▼'; /* Down arrow when expanded */ |
| 44 | + transform: translateY(-50%) rotate(0deg); /* No rotation needed for down arrow */ |
| 45 | + } |
| 46 | + /* Ensure padding is consistent if some links don't have indicators */ |
| 47 | + .sidebar .sidebar-nav a { |
| 48 | + padding-right: 10px; /* Base padding for all links */ |
| 49 | + } |
| 50 | + .sidebar .sidebar-nav li.collapsible > a { |
| 51 | + padding-right: 20px; /* Override for collapsible links */ |
| 52 | + } |
| 53 | + </style> |
14 | 54 | </head> |
15 | 55 |
|
16 | 56 | <body> |
|
68 | 108 |
|
69 | 109 | // =========== Plugins Configuration =========== |
70 | 110 | plugins: [ |
71 | | - // Existing EditOnGithubPlugin |
| 111 | + // 1. Existing EditOnGithubPlugin |
72 | 112 | EditOnGithubPlugin.create( |
73 | 113 | "https://github.com/Snailclimb/JavaGuide/blob/master/" |
74 | 114 | ), |
75 | 115 |
|
76 | | - // --- Plugin for Image Hotlink Protection (from previous request) --- |
| 116 | + // 3. New Plugin to Add referrerpolicy="no-referrer" and Implement Collapsible Sidebar |
77 | 117 | function (hook) { |
78 | | - const targetOrigin = 'https://oss.javaguide.cn/'; |
79 | | - const antiHotlinkParamKeyVal = 'x-oss-process=style/javaguide'; // <-- Adjust this parameter as needed |
80 | | - |
| 118 | + // This part adds referrerpolicy="no-referrer" to all img tags |
81 | 119 | hook.afterEach(function (html, next) { |
82 | | - const imgRegex = new RegExp(`<img[^>]*src="(https?:\\/\\/oss\\.javaguide\\.cn\\/[^"]*)"[^>]*>`, 'gi'); |
83 | | - |
84 | | - let modifiedHtml = html.replace(imgRegex, function(match, srcUrl) { |
85 | | - if (srcUrl.includes(antiHotlinkParamKeyVal)) { |
| 120 | + const imgTagRegex = /<img[^>]*>/gi; |
| 121 | + let modifiedHtml = html.replace(imgTagRegex, function(match) { |
| 122 | + if (match.toLowerCase().includes('referrerpolicy=')) { |
86 | 123 | return match; |
87 | 124 | } |
88 | | - const separator = srcUrl.includes('?') ? '&' : '?'; |
89 | | - const newSrcUrl = srcUrl + separator + antiHotlinkParamKeyVal; |
90 | | - return match.replace(srcUrl, newSrcUrl); |
| 125 | + return match.replace(/>$/, ' referrerpolicy="no-referrer">'); |
91 | 126 | }); |
92 | | - // Note: This plugin now adds referrerpolicy as well. You could combine |
93 | | - // the logic here, or keep it separate in the next plugin. |
94 | | - // Keeping it separate for clarity in demonstrating the new requirement. |
95 | | - |
96 | 127 | next(modifiedHtml); |
97 | 128 | }); |
98 | | - }, |
99 | | - // --- End Plugin for Image Hotlink Protection --- |
100 | | - |
101 | 129 |
|
102 | | - // --- New Plugin to Add referrerpolicy="no-referrer" to ALL Images --- |
103 | | - function (hook) { |
104 | | - hook.afterEach(function (html, next) { |
105 | | - // Regex to find all img tags globally and case-insensitively |
106 | | - const imgTagRegex = /<img[^>]*>/gi; |
107 | | - let modifiedHtml = html.replace(imgTagRegex, function(match) { |
108 | | - // Check if referrerpolicy attribute is already present (case-insensitive) |
109 | | - if (match.toLowerCase().includes('referrerpolicy=')) { |
110 | | - return match; // Attribute already exists, return original match |
| 130 | + // This part implements the collapsible sidebar logic |
| 131 | + // Use doneEach or ready hook to ensure sidebar DOM is available |
| 132 | + hook.doneEach(function() { // Or hook.ready(function() { ... }); |
| 133 | + const sidebar = document.querySelector('.sidebar'); |
| 134 | + if (!sidebar) return; // Exit if sidebar element is not found |
| 135 | + |
| 136 | + // Find all LIs that contain a direct child UL (these are the parent items with nested menus) |
| 137 | + // Use querySelector(':scope > ul') for checking direct children. |
| 138 | + // Fallback for older browsers that don't support :scope (though uncommon now) |
| 139 | + const allLIs = sidebar.querySelectorAll('.sidebar-nav li'); |
| 140 | + const collapsibleItems = []; |
| 141 | + allLIs.forEach(li => { |
| 142 | + // Check if this LI contains a UL as a direct child |
| 143 | + if (li.querySelector(':scope > ul')) { |
| 144 | + collapsibleItems.push(li); |
| 145 | + } |
| 146 | + }); |
| 147 | + |
| 148 | + // Process each potential collapsible item |
| 149 | + collapsibleItems.forEach(li => { |
| 150 | + li.classList.add('collapsible'); // Mark it with a class |
| 151 | + |
| 152 | + // Find the direct link within this collapsible LI |
| 153 | + const link = li.querySelector(':scope > a'); // Direct child link |
| 154 | + if (link) { |
| 155 | + // Add click listener to the link |
| 156 | + link.addEventListener('click', function(event) { |
| 157 | + // Toggle the 'expanded' class on the parent LI (li is available via closure) |
| 158 | + li.classList.toggle('expanded'); |
| 159 | + |
| 160 | + // --- IMPORTANT --- |
| 161 | + // Decide if clicking this link should *also* navigate to the page it links to. |
| 162 | + // - If this link represents a *category* title that doesn't have its own page, |
| 163 | + // and clicking it should *only* toggle the submenu: |
| 164 | + // event.preventDefault(); |
| 165 | + // - If this link represents a *page* (e.g., an index page for the category), |
| 166 | + // and clicking it should *both* toggle the submenu *and* navigate: |
| 167 | + // Remove event.preventDefault(); // (Current state in the code) |
| 168 | + // --- End IMPORTANT --- |
| 169 | + |
| 170 | + // event.preventDefault(); // Uncomment this line if clicking should only toggle collapse |
| 171 | + |
| 172 | + // Stop event from bubbling up, preventing potential interference |
| 173 | + event.stopPropagation(); |
| 174 | + }); |
111 | 175 | } |
112 | | - // Add the attribute right before the closing '>' |
113 | | - // This replaces the last '>' with the attribute and '>' |
114 | | - return match.replace(/>$/, ' referrerpolicy="no-referrer">'); |
| 176 | + // Note: Adding click listener to the LI itself might be another approach, |
| 177 | + // but clicking the link within the LI is usually the intended interaction point. |
| 178 | + // Adding it to the link also allows using the link's default behavior if needed. |
115 | 179 | }); |
116 | 180 |
|
117 | | - next(modifiedHtml); |
| 181 | + // --- Handle initial expansion of the active path --- |
| 182 | + // Find the currently active link in the sidebar |
| 183 | + const activeLink = sidebar.querySelector('.sidebar-nav .active'); |
| 184 | + if (activeLink) { |
| 185 | + // Start from the LI containing the active link |
| 186 | + let currentElement = activeLink.parentElement; |
| 187 | + // Traverse up the DOM tree towards the sidebar root |
| 188 | + while (currentElement && currentElement !== sidebar) { |
| 189 | + // If the current element is an LI AND it has the 'collapsible' class |
| 190 | + if (currentElement.tagName === 'LI' && currentElement.classList.contains('collapsible')) { |
| 191 | + currentElement.classList.add('expanded'); // Expand this parent collapsible LI |
| 192 | + } |
| 193 | + // Move up to the next parent element |
| 194 | + currentElement = currentElement.parentElement; |
| 195 | + } |
| 196 | + } |
| 197 | + // --- End initial expansion logic --- |
118 | 198 | }); |
119 | 199 | } |
120 | | - // --- End New Plugin --- |
121 | | - |
| 200 | + // --- End Collapsible Sidebar Plugin --- |
122 | 201 | ], |
123 | 202 | // =========================================== |
124 | 203 | }; |
|
0 commit comments