1// ç½ç«é®åä¸ç»è®¡ 2document.addEventListener("DOMContentLoaded", function () { 3 // ç½ç«é®å 4 const weekday = ["卿¥", "å¨ä¸", "å¨äº", "å¨ä¸", "å¨å", "å¨äº", "å¨å "]; 5 const today = new Date(); 6 const hrs = today.getHours(); 7 const date = `${today.getFullYear()}å¹´${(today.getMonth() + 1).toString().padStart(2, '0')}æ${today.getDate().toString().padStart(2, '0')}æ¥`; 8 const siteName = window.CONFIG && window.CONFIG.siteName ? window.CONFIG.siteName : "NOISE导èª"; 9 const greet = hrs < 5 ? `忍好,欢è¿è®¿é® ${siteName}` : 10 hrs < 9 ? `æ©ä¸å¥½,欢è¿è®¿é® ${siteName}` : 11 hrs < 11 ? `ä¸å好,欢è¿è®¿é® ${siteName}` : 12 hrs < 13 ? `ä¸å好,欢è¿è®¿é® ${siteName}` : 13 hrs < 17 ? `ä¸å好,欢è¿è®¿é® ${siteName}` : 14 hrs < 19 ? `åæå¥½,欢è¿è®¿é® ${siteName}` : 15 hrs < 22 ? `æä¸å¥½,欢è¿è®¿é® ${siteName}` : `${siteName}æéæ¨ï¼å¤æ·±äºï¼æ©ç¹ä¼æ¯å¦ð¯`; 16 const lbl = document.getElementById('lbl'); 17 if (lbl) { 18 lbl.textContent = ''; 19 lbl.appendChild(document.createTextNode(greet)); 20 const dateDiv = document.createElement('div'); 21 dateDiv.id = 'date'; 22 dateDiv.textContent = 'ä»å¤©æ¯:' + date; 23 lbl.appendChild(dateDiv); 24 } 25 26 // ç½ç«ç»è®¡API软ç¼ç 27 const statisticsApi = window.CONFIG && window.CONFIG.statisticsApi ? window.CONFIG.statisticsApi : ""; 28 const statisticsMode = window.CONFIG && window.CONFIG.statisticsMode ? window.CONFIG.statisticsMode : ""; 29 const localCountRaw = window.CONFIG && window.CONFIG.statisticsLocalUrlCount !== undefined ? window.CONFIG.statisticsLocalUrlCount : null; 30 const localCount = typeof localCountRaw === 'number' ? localCountRaw : Number(localCountRaw); 31 if (statisticsMode === 'local') { 32 const dateDiv = document.getElementById('date'); 33 if (Number.isFinite(localCount) && localCount > 0) { 34 if (dateDiv && !dateDiv.innerHTML.includes('æ¬ç«å·²æ¶å½')) { 35 dateDiv.innerHTML += ` æ¬ç«å·²æ¶å½:${localCount}个ç½ç«`; 36 } 37 } else { 38 resolveLocalStatisticsCount() 39 .then(function (count) { 40 if (dateDiv && Number.isFinite(count) && count > 0 && !dateDiv.innerHTML.includes('æ¬ç«å·²æ¶å½')) { 41 dateDiv.innerHTML += ` æ¬ç«å·²æ¶å½:${count}个ç½ç«`; 42 } 43 }) 44 .catch(function () { 45 // Ignore local count fetch errors and keep header usable. 46 }); 47 } 48 } else if (statisticsApi) { 49 fetch(statisticsApi) 50 .then(response => response.json()) 51 .then(data => { 52 const urlCount = data.urlCount; 53 const dateDiv = document.getElementById('date'); 54 if (dateDiv) { 55 if (!dateDiv.innerHTML.includes('æ¬ç«å·²æ¶å½')) { 56 dateDiv.innerHTML += ` æ¬ç«å·²æ¶å½:${urlCount}个ç½ç«`; 57 } 58 } 59 }) 60 .catch(error => { 61 const dateDiv = document.getElementById('date'); 62 if (dateDiv) dateDiv.innerHTML += ` æ æ³è·åç½ç«æ°é`; 63 }); 64 } 65 66 // æè¿æ¶å½ç½ç«åè½ 67 fetchRecentSites(); 68 69 // æç´¢æ¨¡åäºä»¶ç»å® 70 const searchInput = document.getElementById('search-input'); 71 const searchButton = document.getElementById('search-button'); 72 const searchResults = document.getElementById('search-results'); 73 const overlay = document.getElementById('overlay'); 74 let resultsHeader = null; 75 if (searchResults) { 76 resultsHeader = searchResults.querySelector('.results-header'); 77 } 78 79 if (searchButton && searchInput) { 80 searchButton.addEventListener('click', performSearch); 81 searchInput.addEventListener('keydown', function (e) { 82 if (e.key === 'Enter') { 83 performSearch(); 84 } 85 }); 86 } 87 if (overlay && searchResults) { 88 overlay.addEventListener('click', function () { 89 searchResults.style.display = 'none'; 90 overlay.style.display = 'none'; 91 }); 92 } 93 94 // è¿ææ¶å½å¼¹çª 95 fetchAndShowLatestNotification(); 96 setInterval(function () { 97 if (document.visibilityState === 'visible') { 98 fetchAndShowLatestNotification(); 99 } 100 }, 60000); 101}); 102 103// --- æè¿æ¶å½ç½ç«ç¸å ³ --- 104function buildNoCacheUrl(rawUrl) { 105 try { 106 var url = new URL(rawUrl, window.location.href); 107 url.searchParams.set('_t', Date.now().toString()); 108 return url.toString(); 109 } catch (e) { 110 var sep = rawUrl.indexOf('?') >= 0 ? '&' : '?'; 111 return rawUrl + sep + '_t=' + Date.now(); 112 } 113} 114 115function escapeHtml(value) { 116 const div = document.createElement('div'); 117 div.textContent = value == null ? '' : String(value); 118 return div.innerHTML; 119} 120
121function safeExternalHref(rawUrl) { 122 const value = String(rawUrl == null ? '' : rawUrl).trim(); 123 if (!value) return '#'; 124 if (value.startsWith('#')) return value; 125 try { 126 const u = new URL(value, window.location.href); 127 const protocol = (u.protocol || '').toLowerCase(); 128 if (protocol === 'http:' || protocol === 'https:') return u.toString(); 129 return '#'; 130 } catch (e) { 131 if (value.startsWith('/')) return value; 132 return '#'; 133 } 134} 135 136async function fetchWithTimeout(url, options, timeoutMs) { 137 const controller = new AbortController(); 138 const ms = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 1500; 139 const timer = setTimeout(function () { 140 try { controller.abort(); } catch (e) {} 141 }, ms); 142 try { 143 const opts = Object.assign({}, options || {}, { signal: controller.signal }); 144 return await fetch(url, opts); 145 } finally { 146 clearTimeout(timer); 147 } 148} 149 150function normalizeSiteUrl(url) { 151 return String(url == null ? '' : url) 152 .trim() 153 .toLowerCase() 154 .replace(/^https?:\/\//, '') 155 .replace(/\/+$/, ''); 156} 157 158async function resolveLocalStatisticsCount() { 159 const items = await ensureLocalSearchIndex(); 160 if (!Array.isArray(items) || !items.length) { 161 return null; 162 } 163 164 const uniqueUrls = new Set(); 165 for (let j = 0; j < items.length; j++) { 166 const item = items[j] || {}; 167 if (String(item.taxonomy || '') === 'åæ é¾æ¥') continue; 168 const normalizedUrl = normalizeSiteUrl(item.url); 169 if (normalizedUrl) uniqueUrls.add(normalizedUrl); 170 } 171 172 return uniqueUrls.size || null; 173} 174 175async function fetchRecentSites() { 176 const siteListElement = document.getElementById('site-list'); 177 const titleElement = document.getElementById('recent-sites-title'); 178 const recentSitesElement = document.getElementById('recent-sites'); 179 if (!siteListElement || !titleElement || !recentSitesElement) return; 180 181 const cachedData = localStorage.getItem('recentSites'); 182 const cachedTime = localStorage.getItem('recentSitesTimestamp'); 183 let hasRenderedCache = false; 184 if (cachedData && cachedTime) { 185 try { 186 displaySites(JSON.parse(cachedData)); 187 hasRenderedCache = true; 188 } catch (e) { 189 hasRenderedCache = false; 190 } 191 } 192 193 try { 194 const controller = new AbortController(); 195 const timeoutId = setTimeout(() => controller.abort(), 5000); 196 const recentSitesApi = window.CONFIG && window.CONFIG.recentSitesApi ? window.CONFIG.recentSitesApi : 'https://extension.noisework.cn/api/notifications'; 197 const response = await fetch(buildNoCacheUrl(recentSitesApi), { 198 signal: controller.signal, 199 cache: 'no-store' 200 }); 201 clearTimeout(timeoutId); 202 203 if (!response.ok) { 204 throw new Error('ç½ç»ååºä¸æ£å¸¸: ' + response.status); 205 } 206 207 const data = await response.json(); 208 const nextCache = JSON.stringify(data); 209 if (!cachedData || cachedData !== nextCache) { 210 displaySites(data); 211 } 212 localStorage.setItem('recentSites', nextCache); 213 localStorage.setItem('recentSitesTimestamp', Date.now()); 214 } catch (error) { 215 if (!hasRenderedCache) { 216 if (titleElement) titleElement.style.display = 'none'; 217 if (recentSitesElement) recentSitesElement.style.display = 'none'; 218 } 219 } 220} 221 222function displaySites(data) { 223 const siteListElement = document.getElementById('site-list'); 224 const titleElement = document.getElementById('recent-sites-title'); 225 const recentSitesElement = document.getElementById('recent-sites'); 226 if (!siteListElement || !titleElement || !recentSitesElement) return; 227 siteListElement.innerHTML = ''; 228 siteListElement.dataset.scrolling = ''; 229 siteListElement.scrollTop = 0; 230 231 if (data.message) { 232 const messageItem = document.createElement('li'); 233 messageItem.textContent = data.message; 234 siteListElement.appendChild(messageItem); 235 titleElement.style.display = ''; 236 recentSitesElement.style.display = ''; 237 return; 238 } 239 240 data.forEach(site => { 241 const listItem = document.createElement('li'); 242 const title = document.createElement('strong'); 243 title.className = 'title'; 244 title.textContent = String((site && site.title) ? site.title : '') + 'ï¼'; 245 246 const link = document.createElement('a'); 247 link.className = 'url';
248 link.href = safeExternalHref(site && site.url); 249 link.target = '_blank'; 250 link.rel = 'noopener noreferrer'; 251 link.textContent = String((site && site.url) ? site.url : ''); 252 253 const sep = document.createTextNode('ï¼'); 254 255 const desc = document.createElement('span'); 256 desc.className = 'description'; 257 desc.textContent = String((site && site.description) ? site.description : 'æ æè¿°'); 258 259 listItem.appendChild(title); 260 listItem.appendChild(link); 261 listItem.appendChild(sep); 262 listItem.appendChild(desc); 263 siteListElement.appendChild(listItem); 264 }); 265 266 titleElement.style.display = ''; 267 recentSitesElement.style.display = ''; 268 startScrolling(siteListElement); 269} 270 271function startScrolling(element) { 272 if (!element || element.dataset.scrolling === 'true') return; 273 const scrollHeight = element.scrollHeight; 274 const visibleHeight = element.clientHeight; 275 const maxScrollTop = Math.max(0, scrollHeight - visibleHeight); 276 let scrollTop = 0; 277 const reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 278 if (reduceMotion || scrollHeight <= visibleHeight + 4) return; 279 element.dataset.scrolling = 'true'; 280 let lastFrameTime = 0; 281 function scroll(now) { 282 if (document.visibilityState !== 'visible') { 283 requestAnimationFrame(scroll); 284 return; 285 } 286 if (now - lastFrameTime < 42) { 287 requestAnimationFrame(scroll); 288 return; 289 } 290 lastFrameTime = now; 291 scrollTop += 0.3; 292 if (scrollTop >= maxScrollTop) { 293 scrollTop = 0; 294 } 295 element.scrollTop = scrollTop; 296 requestAnimationFrame(scroll); 297 } 298 requestAnimationFrame(scroll); 299} 300 301// --- æ¬å°æç´¢ç¸å ³ --- 302let _localSearchIndex = null; 303let _localSearchIndexLoaded = false; 304let _localSearchIndexLoadPromise = null; 305let _localSearchIndexScriptPromise = null; 306 307function coerceSearchIndexPayload(payload) { 308 if (Array.isArray(payload)) return payload; 309 if (payload && Array.isArray(payload.results)) return payload.results; 310 if (payload && Array.isArray(payload.data)) return payload.data; 311 if (typeof payload === 'string') { 312 try { 313 return coerceSearchIndexPayload(JSON.parse(payload)); 314 } catch (e) { 315 return null; 316 } 317 } 318 return null; 319} 320 321function readLocalSearchIndexFromDom() { 322 if (typeof window !== 'undefined' && window.__HEADER_SEARCH_INDEX__) { 323 const fromWindow = coerceSearchIndexPayload(window.__HEADER_SEARCH_INDEX__); 324 if (Array.isArray(fromWindow)) return fromWindow; 325 } 326 try { 327 var el = document.getElementById('header-search-index'); 328 var raw = el ? (el.textContent || el.innerText || '') : ''; 329 return coerceSearchIndexPayload(raw ? JSON.parse(raw) : []); 330 } catch (e) { 331 return null; 332 } 333} 334 335async function fetchLocalSearchIndexFromFiles() { 336 if (window.location.protocol === 'file:') { 337 return []; 338 } 339 const configSearchIndexUrl = window.CONFIG && window.CONFIG.searchIndexUrl ? String(window.CONFIG.searchIndexUrl) : ''; 340 const origin = window.location.origin || ''; 341 const pathname = window.location.pathname || '/'; 342 const baseDir = pathname.replace(/\/[^/]*$/, '/') || '/'; 343 const candidates = [ 344 configSearchIndexUrl, 345 origin + '/search-index.json', 346 origin + '/index.searchindex.json', 347 '/search-index.json', 348 '/index.searchindex.json', 349 'search-index.json', 350 '../search-index.json', 351 '../../search-index.json', 352 baseDir + 'search-index.json', 353 baseDir + 'index.searchindex.json' 354 ]; 355 const seen = new Set(); 356 357 for (let i = 0; i < candidates.length; i++) { 358 const url = candidates[i]; 359 if (!url || seen.has(url)) continue; 360 seen.add(url); 361 try { 362 const resp = await fetch(url, { cache: 'no-store' }); 363 if (!resp.ok) continue; 364 const parsed = coerceSearchIndexPayload(await resp.json()); 365 if (Array.isArray(parsed) && parsed.length) { 366 return parsed; 367 } 368 } catch (e) { 369 // Try the next generated Hugo index path. 370 } 371 } 372 373 return []; 374} 375 376function ensureLocalSearchIndexScript() { 377 const existingIndex = readLocalSearchIndexFromDom(); 378 if (Array.isArray(existingIndex) && existingIndex.length) { 379 return Promise.resolve(existingIndex); 380 } 381 382 if (_localSearchIndexScriptPromise) { 383 return _localSearchIndexScriptPromise; 384 } 385 386 _localSearchIndexScriptPromise = new Promise(function (resolve) { 387 const scriptUrl = window.CONFIG && window.CONFIG.searchIndexScriptUrl ? String(window.CONFIG.searchIndexScriptUrl) : 'search-index.js'; 388 const existingScript = document.getElementById('header-search-index-js'); 389 390 function finish() { 391 const loadedIndex = readLocalSearchIndexFromDom(); 392 resolve(Array.isArray(loadedIndex) ? loadedIndex : []); 393 } 394 395 if (existingScript) { 396 if (Array.isArray(window.__HEADER_SEARCH_INDEX__) && window.__HEADER_SEARCH_INDEX__.length) { 397 finish(); 398 return; 399 } 400 existingScript.addEventListener('load', finish, { once: true }); 401 existingScript.addEventListener('error', function () { 402 resolve([]); 403 }, { once: true }); 404 return; 405 } 406 407 const script = document.createElement('script'); 408 script.id = 'header-search-index-js'; 409 script.src = scriptUrl; 410 script.async = false;
411 script.onload = finish; 412 script.onerror = function () { 413 resolve([]); 414 }; 415 document.head.appendChild(script); 416 }).finally(function () { 417 _localSearchIndexScriptPromise = null; 418 }); 419 420 return _localSearchIndexScriptPromise; 421} 422 423async function ensureLocalSearchIndex() { 424 if (_localSearchIndexLoaded) return _localSearchIndex || []; 425 if (_localSearchIndexLoadPromise) return _localSearchIndexLoadPromise; 426 _localSearchIndexLoadPromise = (async function () { 427 var index = readLocalSearchIndexFromDom(); 428 if (!Array.isArray(index) || !index.length) { 429 index = await ensureLocalSearchIndexScript(); 430 } 431 if (!Array.isArray(index) || !index.length) { 432 index = await fetchLocalSearchIndexFromFiles(); 433 } 434 435 _localSearchIndex = Array.isArray(index) ? index : []; 436 _localSearchIndexLoaded = true; 437 _localSearchIndexLoadPromise = null; 438 return _localSearchIndex; 439 })(); 440 return _localSearchIndexLoadPromise; 441} 442 443function escapeHeaderHtml(value) { 444 const div = document.createElement('div'); 445 div.textContent = value == null ? '' : String(value); 446 return div.innerHTML; 447} 448 449function normalizeSearchField(value) { 450 return value == null ? '' : String(value); 451} 452 453function normalizeSearchQuery(keyword) { 454 let query = String(keyword || '') 455 .toLowerCase() 456 .replace(/[ï¼,ã.;ï¼ã/|]+/g, ' ') 457 .replace(/\s+/g, ' ') 458 .trim(); 459 if (!query) return ''; 460 query = query 461 .replace(/^(ææ²¡æ|æä»ä¹|æåªäº|请|帮æ|æ¨è|æç´¢|æä¸ä¸|æ¾ä¸ä¸|æ¾æ¾|ææ³|请é®|è½ä¸è½|å¯ä»¥|æä¹æ ·|å¦ä½|æä¹)\s*/g, '') 462 .replace(/\s*(å¢|å|å§|å|å|å|ä¹)$/g, '') 463 .trim(); 464 return query; 465} 466 467function extractSearchTerms(keyword) { 468 const normalized = normalizeSearchQuery(keyword); 469 if (!normalized) return []; 470 471 const terms = new Set(); 472 normalized.split(/\s+/).filter(Boolean).forEach(function (part) { 473 terms.add(part); 474 475 const cjkSegments = part.match(/[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]+/g) || []; 476 cjkSegments.forEach(function (seg) { 477 if (seg.length === 1) { 478 terms.add(seg); 479 return; 480 } 481 if (seg.length <= 4) terms.add(seg); 482 for (let i = 0; i < seg.length - 1; i++) { 483 terms.add(seg.slice(i, i + 2)); 484 } 485 }); 486 487 const latinWords = part.match(/[a-z0-9]+/g) || []; 488 latinWords.forEach(function (word) { 489 if (word) terms.add(word); 490 }); 491 }); 492 493 return Array.from(terms).filter(Boolean); 494} 495 496function doLocalSearch(keyword) { 497 if (!_localSearchIndex || !_localSearchIndex.length) return []; 498 499 const terms = extractSearchTerms(keyword); 500 if (!terms.length) return []; 501 502 const results = []; 503 504 for (let i = 0; i < _localSearchIndex.length; i++) { 505 const item = _localSearchIndex[i]; 506 const titleValue = normalizeSearchField(item.title); 507 const descValue = normalizeSearchField(item.description); 508 const taxonomyValue = normalizeSearchField(item.taxonomy); 509 const termValue = normalizeSearchField(item.term); 510 const urlValue = normalizeSearchField(item.url); 511 const title = titleValue.toLowerCase(); 512 const desc = descValue.toLowerCase(); 513 const taxonomy = taxonomyValue.toLowerCase(); 514 const term = termValue.toLowerCase(); 515 const url = urlValue.toLowerCase(); 516 517 let score = 0; 518 for (let t = 0; t < terms.length; t++) { 519 const q = terms[t]; 520 if (!q) continue; 521 let matched = false; 522 if (title.includes(q)) { 523 score += title === q ? 18 : 10; 524 matched = true; 525 } 526 if (term.includes(q)) { 527 score += term === q ? 10 : 6; 528 matched = true; 529 } 530 if (taxonomy.includes(q)) { 531 score += taxonomy === q ? 9 : 5; 532 matched = true; 533 } 534 if (desc.includes(q)) { 535 score += 3; 536 matched = true; 537 } 538 if (url.includes(q)) { 539 score += 1; 540 matched = true; 541 } 542 if (!matched) score -= 1; 543 } 544 545 if (score > 0) { 546 results.push({ 547 _score: score, 548 title: titleValue, 549 url: urlValue, 550 description: descValue, 551 taxonomy: taxonomyValue, 552 term: termValue 553 }); 554 } 555 } 556 557 results.sort((a, b) => b._score - a._score); 558 return results; 559} 560 561// --- æç´¢ç¸å ³ --- 562async function performSearch() { 563 const searchInput = document.getElementById('search-input'); 564 const searchResults = document.getElementById('search-results');
565 const overlay = document.getElementById('overlay'); 566 const resultsHeader = searchResults ? searchResults.querySelector('.results-header') : null; 567 if (!searchInput || !searchResults) return; 568 569 await ensureLocalSearchIndex(); 570 571 const keyword = searchInput.value.trim(); 572 if (keyword) { 573 // ä½¿ç¨æ¬å°æç´¢ 574 const results = doLocalSearch(keyword); 575 displaySearchResults(results, searchResults, overlay, resultsHeader); 576 } else { 577 displaySearchResults([], searchResults, overlay, resultsHeader); 578 } 579} 580 581if (typeof window !== 'undefined') { 582 window.performSearch = performSearch; 583} 584 585function displaySearchResults(results, searchResults, overlay, resultsHeader) { 586 if (!searchResults) return; 587 searchResults.innerHTML = ''; 588 if (resultsHeader) { 589 if (Array.isArray(results)) { 590 resultsHeader.textContent = results.length ? ('å½åæç´¢ç»æå¦ä¸ï¼' + results.length + 'ï¼') : 'å½åæç´¢ç»æå¦ä¸ï¼'; 591 } 592 resultsHeader.style.display = "block"; 593 searchResults.appendChild(resultsHeader); 594 } 595 if (Array.isArray(results) && results.length > 0) { 596 results.forEach(result => { 597 const div = document.createElement('div'); 598 div.classList.add('result-item'); 599 const meta = [result.taxonomy, result.term].filter(Boolean).join(' / '); 600 div.innerHTML = ` 601 <div class="result-title">${escapeHeaderHtml(result.title || 'æªå½å')}${meta ? `<span style="font-size:11px;opacity:0.65;margin-left:6px;">${escapeHeaderHtml(meta)}</span>` : ''}</div> 602 <div class="result-url"><a href="${escapeHeaderHtml(safeExternalHref(result.url || '#'))}" target="_blank" rel="noopener noreferrer">${escapeHeaderHtml(result.url || '#')}</a></div> 603 <div class="result-description">${escapeHeaderHtml(result.description || 'æ æè¿°')}</div> 604 `; 605 searchResults.appendChild(div); 606 }); 607 } else { 608 const div = document.createElement('div'); 609 div.textContent = "æªæ¾å°ç¸å ³ç»æ"; 610 searchResults.appendChild(div); 611 } 612 searchResults.style.display = "flex"; 613 if (overlay) overlay.style.display = "block"; 614} 615 616// --- è¿ææ¶å½å¼¹çªï¼åªä¿çå³ä¸è§iziToastï¼--- 617let shownNotifications = []; 618try { 619 shownNotifications = JSON.parse(localStorage.getItem('shownNotifications')) || []; 620} catch (e) { 621 shownNotifications = []; 622} 623 624async function fetchAndShowLatestNotification() { 625 try { 626 const recentSitesApi = window.CONFIG && window.CONFIG.recentSitesApi ? window.CONFIG.recentSitesApi : 'https://extension.noisework.cn/api/notifications'; 627 const response = await fetchWithTimeout(buildNoCacheUrl(recentSitesApi), { cache: 'no-store' }, 1500); 628 if (!response || !response.ok) return; 629 const notifications = await response.json(); 630 631 const bellIcon = document.getElementById('noisenotification-icon'); 632 let hasNewNotification = false; 633 634 if (Array.isArray(notifications) && notifications.length > 0) { 635 const latestNotification = notifications.reduce((latest, current) => { 636 const currentTime = new Date(current.timestamp || current.date || 0); 637 const latestTime = new Date(latest.timestamp || latest.date || 0); 638 return currentTime > latestTime ? current : latest; 639 }); 640 641 const safeTitle = escapeHtml(latestNotification && latestNotification.title); 642 const safeDesc = escapeHtml((latestNotification && latestNotification.description) ? latestNotification.description : (latestNotification && latestNotification.url)); 643 const safeHref = safeExternalHref(latestNotification && latestNotification.url); 644 const notificationContent = 'ðç«ç¹æ¶å½æ´æ°éç¥ï¼<strong>' + safeTitle + '</strong><br><a href="' + escapeHtml(safeHref) + '" target="_blank" rel="noopener noreferrer" style="color:#ff9800;text-decoration:underline;">' + safeDesc + '</a>'; 645 646 // 馿¬¡è®¿é® shownNotifications 为空ï¼å¿ å¼¹ 647 if (!shownNotifications.includes(notificationContent)) { 648 shownNotifications.push(notificationContent); 649 // éå¶ç¼åé¿åº¦ï¼é²æ¢æ éå¢é¿ 650 if (shownNotifications.length > 20) shownNotifications.shift(); 651 localStorage.setItem('shownNotifications', JSON.stringify(shownNotifications)); 652 if (window.iziToast) { 653 iziToast.show({ 654 title: false, 655 message: notificationContent, 656 position: 'topRight', 657 timeout: 5000, 658 messageColor: '#222', 659 backgroundColor: '#fff', 660 icon: 'icon-star' 661 }); 662 } 663 hasNewNotification = true; 664 } 665 } 666 // æ§å¶éé徿 é¢è² 667 if (bellIcon) { 668 if (hasNewNotification) { 669 bellIcon.classList.add('active'); 670 // æ è®°ææ°éç¥ 671 localStorage.setItem('hasNewNotification', '1'); 672 } else if (localStorage.getItem('hasNewNotification') === '1') { 673 // åªè¦æ¬å°ææªè¯»éç¥ï¼ä¹ä¿æåè² 674 bellIcon.classList.add('active'); 675 } else { 676 bellIcon.classList.remove('active'); 677 } 678 } 679 // å¯éï¼çå¬ç¨æ·ç¹å»ééï¼æ¸ 餿ªè¯»æ è®° 680 if (bellIcon) { 681 bellIcon.onclick = function() { 682 bellIcon.classList.remove('active'); 683 localStorage.removeItem('hasNewNotification'); 684 }; 685 } 686 } catch (error) { 687 console.error('è·åéç¥å¤±è´¥:', error); 688 } 689} 690 691function syncHeaderPageMode(activeKey) { 692 var headerTop = document.querySelector('.header-top'); 693 if (!headerTop) return; 694 var isDefault = !activeKey || activeKey === 'default'; 695 headerTop.classList.toggle('is-default-page', isDefault); 696 headerTop.classList.toggle('is-page-active', !isDefault); 697 queueSyncHeaderColumnHeights(); 698} 699 700var headerHeightResizeObserver = null; 701var headerHeightMutationObserver = null; 702var cachedDefaultSideHeight = 0; 703var cachedDefaultHeaderTopHeight = 0; 704var headerHeightSyncRafId = 0; 705var headerHeightSyncTimerId = 0;
706var archiveScrollLockUntil = 0; 707var archiveScrollProgrammaticUntil = 0; 708var headerHeightMode = ''; 709 710function clearHeaderPageHeightStyles(side) { 711 if (!side) return; 712 var region = side.querySelector('.header-page-region'); 713 if (region) { 714 region.style.height = ''; 715 region.style.maxHeight = ''; 716 } 717 side.querySelectorAll('.page, .page-shell, .archive-scroll, .hot-page-shell, .hot-page-panels, .hot-page-panel, .hot-page-panel-body').forEach(function (node) { 718 node.style.height = ''; 719 node.style.maxHeight = ''; 720 }); 721} 722 723function syncHeaderColumnHeights() { 724 var headerTop = document.querySelector('.header-top'); 725 if (!headerTop) return; 726 var main = headerTop.querySelector('.header-top-main'); 727 var side = headerTop.querySelector('.header-top-side'); 728 if (!main || !side) return; 729 730 if (window.innerWidth <= 1024) { 731 if (headerHeightMode !== 'mobile') { 732 headerHeightMode = 'mobile'; 733 headerTop.style.height = ''; 734 headerTop.style.minHeight = ''; 735 headerTop.style.maxHeight = ''; 736 main.style.minHeight = ''; 737 side.style.minHeight = ''; 738 main.style.height = ''; 739 side.style.height = ''; 740 clearHeaderPageHeightStyles(side); 741 } 742 return; 743 } 744 745 headerHeightMode = 'desktop'; 746 747 var activePageBefore = side.querySelector('.page.active'); 748 var activePageIdBefore = activePageBefore && activePageBefore.id ? activePageBefore.id : ''; 749 var archiveScrollBefore = null; 750 var archiveScrollTopBefore = 0; 751 if (activePageIdBefore === 'guidang' && activePageBefore) { 752 archiveScrollBefore = activePageBefore.querySelector('.archive-scroll'); 753 if (archiveScrollBefore) { 754 archiveScrollTopBefore = archiveScrollBefore.scrollTop || 0; 755 } 756 } 757 758 if (Date.now() < archiveScrollLockUntil) { 759 if (activePageIdBefore === 'guidang') { 760 return; 761 } 762 } 763 764 // éç½®ææå èæ ·å¼ä»¥è·åèªç¶é«åº¦ 765 headerTop.style.height = ''; 766 headerTop.style.minHeight = ''; 767 headerTop.style.maxHeight = ''; 768 main.style.minHeight = ''; 769 side.style.minHeight = ''; 770 main.style.height = ''; 771 side.style.height = ''; 772 clearHeaderPageHeightStyles(side); 773 774 void headerTop.offsetHeight; 775 776 // è·åå½åç¶æ 777 var isPageActive = headerTop.classList.contains('is-page-active'); 778 779 // è·å左侧æ çèªç¶é«åº¦ï¼ä½ä¸ºåºåï¼ 780 var mainRect = main.getBoundingClientRect(); 781 var mainHeight = mainRect.height; 782 783 // è·å/æ´æ°é»è®¤å³ä¾§å 容çèªç¶é«åº¦ 784 var defaultSide = side.querySelector('.header-side-default'); 785 if (defaultSide) { 786 if (!isPageActive) { 787 cachedDefaultSideHeight = defaultSide.getBoundingClientRect().height; 788 cachedDefaultHeaderTopHeight = headerTop.getBoundingClientRect().height; 789 } else if (cachedDefaultSideHeight === 0) { 790 // 妿åå§å 载就æ¯åé¡µç¶æï¼å°è¯è·å䏿¬¡ 791 var originalDisplay = defaultSide.style.display; 792 defaultSide.style.display = 'flex'; 793 cachedDefaultSideHeight = defaultSide.getBoundingClientRect().height; 794 defaultSide.style.display = originalDisplay; 795 } 796 } 797 798 // 计ç®ç®æ é«åº¦ 799 // åé¡µæ¿æ´»æ¶ï¼éå®ä¸ºé»è®¤ç¶æä¸çæ´ä½é«åº¦ï¼é¿å çæ¦/iframe å 容ååå¯¼è´æ´ä½é«åº¦è¢«âæçâ 800 // é»è®¤ç¶ææ¶ï¼ä»¥å·¦å³æ æ´é«çä¸ä¾§ä½ä¸ºåºå 801 var targetHeight = isPageActive && cachedDefaultHeaderTopHeight > 0 802 ? cachedDefaultHeaderTopHeight 803 : Math.max(mainHeight, cachedDefaultSideHeight); 804 805 if (targetHeight > 0) { 806 var headerStyles = window.getComputedStyle(headerTop); 807 var headerPaddingTop = parseFloat(headerStyles.paddingTop || '0') || 0; 808 var headerPaddingBottom = parseFloat(headerStyles.paddingBottom || '0') || 0; 809 var innerColumnHeight = Math.max(0, targetHeight - headerPaddingTop - headerPaddingBottom); 810 811 // åºç¨é«åº¦éå®ï¼ç¡®ä¿å·¦å³æ çé«ä¸ä¸å å 容å è½½èéªç 812 headerTop.style.height = targetHeight + 'px'; 813 headerTop.style.minHeight = targetHeight + 'px'; 814 headerTop.style.maxHeight = targetHeight + 'px'; 815 main.style.height = innerColumnHeight + 'px'; 816 side.style.height = innerColumnHeight + 'px'; 817 // åæ¶è®¾ç½® min-height å¢å¼ºç¨³å®æ§ 818 main.style.minHeight = innerColumnHeight + 'px'; 819 side.style.minHeight = innerColumnHeight + 'px'; 820 821 // 妿å页å¤äºæ¿æ´»ç¶æï¼éè¦ç²¾ç¡®è®¡ç®å 鍿»å¨åºåçé«åº¦ 822 if (isPageActive) { 823 var region = side.querySelector('.header-page-region'); 824 var activePageNode = side.querySelector('.page.active'); 825 826 if (region && activePageNode) { 827 // 计ç®å页åºåå¨å³ä¾§æ ä¸çå¯ç¨ç©ºé´ 828 var sideRect = side.getBoundingClientRect(); 829 var regionRect = region.getBoundingClientRect(); 830 var regionTopOffset = regionRect.top - sideRect.top; 831 var availableHeight = Math.max(0, innerColumnHeight - regionTopOffset); 832 833 region.style.height = availableHeight + 'px'; 834 region.style.maxHeight = availableHeight + 'px'; 835 var regionStyles = window.getComputedStyle(region); 836 var regionPaddingTop = parseFloat(regionStyles.paddingTop || '0') || 0; 837 var regionPaddingBottom = parseFloat(regionStyles.paddingBottom || '0') || 0; 838 var regionBorderTop = parseFloat(regionStyles.borderTopWidth || '0') || 0; 839 var regionBorderBottom = parseFloat(regionStyles.borderBottomWidth || '0') || 0; 840 var innerAvailableHeight = Math.max(0, availableHeight - regionPaddingTop
840- regionPaddingBottom - regionBorderTop - regionBorderBottom); 841 842 activePageNode.style.height = innerAvailableHeight + 'px'; 843 activePageNode.style.maxHeight = innerAvailableHeight + 'px'; 844 845 var shell = activePageNode.querySelector('.page-shell'); 846 if (shell) { 847 shell.style.height = innerAvailableHeight + 'px'; 848 shell.style.maxHeight = innerAvailableHeight + 'px'; 849 } 850 851 if (activePageNode.id === 'guidang' && shell) { 852 var archiveShell = shell.classList && shell.classList.contains('archive-page-shell') ? shell : shell.querySelector('.archive-page-shell'); 853 if (archiveShell) { 854 var archiveHeader = archiveShell.querySelector('.archive-header'); 855 var archiveScroll = archiveShell.querySelector('.archive-scroll'); 856 if (archiveHeader && archiveScroll) { 857 var headerHeight = archiveHeader.getBoundingClientRect().height; 858 var scrollHeight = Math.max(0, archiveShell.clientHeight - headerHeight); 859 archiveScroll.style.height = scrollHeight + 'px'; 860 archiveScroll.style.maxHeight = scrollHeight + 'px'; 861 } 862 } 863 } 864 865 // éå¯¹çæ¦é¡µé¢çç¹æ®ç²¾ç¡®è®¡ç® 866 var hotShell = activePageNode.querySelector('.hot-page-shell'); 867 if (hotShell) { 868 hotShell.style.height = innerAvailableHeight + 'px'; 869 hotShell.style.maxHeight = innerAvailableHeight + 'px'; 870 871 var intro = hotShell.querySelector('.hot-page-intro'); 872 var tabs = hotShell.querySelector('.hot-page-tabs'); 873 var panels = hotShell.querySelector('.hot-page-panels'); 874 875 if (panels) { 876 var introHeight = intro ? intro.getBoundingClientRect().height : 0; 877 var tabsHeight = tabs ? tabs.getBoundingClientRect().height : 0; 878 var hotShellStyles = window.getComputedStyle(hotShell); 879 var rowGap = parseFloat(hotShellStyles.rowGap || hotShellStyles.gap || '0') || 0; 880 881 // 计ç®é¢æ¿å®¹å¨é«åº¦ï¼æ»é«åº¦ - 说æåº - 忢æ ç¾ - é´è· 882 var panelsHeight = Math.max(0, innerAvailableHeight - introHeight - tabsHeight - (rowGap * 2)); 883 panels.style.height = panelsHeight + 'px'; 884 panels.style.maxHeight = panelsHeight + 'px'; 885 886 panels.querySelectorAll('.hot-page-panel').forEach(function (panel) { 887 panel.style.height = panelsHeight + 'px'; 888 panel.style.maxHeight = panelsHeight + 'px'; 889 890 var head = panel.querySelector('.hot-page-panel-head'); 891 var updateTime = panel.querySelector('.update-time'); 892 var body = panel.querySelector('.hot-page-panel-body'); 893 894 if (body) { 895 var panelStyles = window.getComputedStyle(panel); 896 var panelGap = parseFloat(panelStyles.rowGap || panelStyles.gap || '0') || 0; 897 var panelPadding = parseFloat(panelStyles.paddingTop || '0') + parseFloat(panelStyles.paddingBottom || '0'); 898 899 var headHeight = head ? head.getBoundingClientRect().height : 0; 900 var timeHeight = updateTime ? updateTime.getBoundingClientRect().height : 0; 901 902 // 计ç®å表主ä½é«åº¦ 903 var bodyHeight = Math.max(0, panelsHeight - headHeight - timeHeight - (panelGap * 2) - panelPadding); 904 body.style.height = bodyHeight + 'px'; 905 body.style.maxHeight = bodyHeight + 'px'; 906 var list = body.querySelector('ol'); 907 if (list) { 908 list.style.height = bodyHeight + 'px'; 909 list.style.maxHeight = bodyHeight + 'px'; 910 list.style.overflowY = 'auto'; 911 list.style.overflowX = 'hidden'; 912 } 913 } 914 }); 915 } 916 } 917 } 918 } 919 } 920 921 if (archiveScrollBefore && archiveScrollTopBefore > 0) { 922 requestAnimationFrame(function () { 923 try { 924 var nowTop = archiveScrollBefore.scrollTop || 0; 925 if (Math.abs(nowTop - archiveScrollTopBefore) <= 1) return; 926 archiveScrollProgrammaticUntil = Date.now() + 220; 927 archiveScrollBefore.scrollTop = archiveScrollTopBefore; 928 requestAnimationFrame(function () { 929 var afterTop = archiveScrollBefore.scrollTop || 0; 930 if (Math.abs(afterTop - archiveScrollTopBefore) <= 1) return; 931 archiveScrollProgrammaticUntil = Date.now() + 220; 932 archiveScrollBefore.scrollTop = archiveScrollTopBefore; 933 }); 934 } catch (e) { 935 } 936 }); 937 } 938} 939 940function queueSyncHeaderColumnHeights() { 941 if (Date.now() < archiveScrollLockUntil) { 942 var headerTop = document.querySelector('.header-top'); 943 if (headerTop && headerTop.classList.contains('is-page-active')) { 944 var activePage = headerTop.querySelector('.header-top-side .page.active'); 945 if (activePage && activePage.id === 'guidang') { 946 return; 947 } 948 } 949 } 950 if (!headerHeightSyncRafId) { 951 headerHeightSyncRafId = requestAnimationFrame(function () { 952 headerHeightSyncRafId = 0; 953 syncHeaderColumnHeights(); 954 }); 955 } 956 clearTimeout(headerHeightSyncTimerId); 957 headerHeightSyncTimerId = setTimeout(syncHeaderColumnHeights, 180); 958} 959 960function observeHeaderColumnChanges() { 961 var headerTop = document.querySelector('.header-top'); 962 if (headerHeightResizeObserver) { 963 headerHeightResizeObserver.disconnect(); 964 headerHeightResizeObserver = null; 965 } 966 if (headerHeightMutationObserver) { 967 headerHeightMutationObserver.disconnect(); 968 headerHeightMutationObserver = null; 969 } 970 if (!headerTop || window.innerWidth <= 1024) return; 971 var main = headerTop.querySelector('.header-top-main'); 972 var side = headerTop.querySelector('.header-top-side'); 973 if (!main || !side) return; 974 975 if ('ResizeObserver' in window) { 976 headerHeightResizeObserver = new ResizeObserver(function () { 977 try { 978 if (headerTop && headerTop.classList && headerTop.classList.contains('is-page-active')) return; 979 } catch (e) { 980 } 981 queueSyncHeaderColumnHeights(); 982 }); 983 headerHeightResizeObserver.observe(main); 984 headerHeightResizeObserver.observe(side); 985 var activePage = side.querySelector('.page.active'); 986 var defaultSide = side.querySelector('.header-side-default'); 987 if (activePage) { 988 headerHeightResizeObserver.observe(activePage); 989 } 990 if (defaultSide) { 991 headerHeightResizeObserver.observe(defaultSide); 992 } 993 } 994 995 headerHeightMutationObserver = new MutationObserver(function () { 996 try { 997 if (headerTop && headerTop.classList && headerTop.classList.contains('is-page-active')) return; 998 } catch (e) { 999 } 1000 queueSyncHeaderColumnHeights(); 1001 }); 1002 headerHeightMutationObserver.observe(side, { 1003 childList: true, 1004 subtree: true 1005 }); 1006} 1007 1008function switchHotPanel(target) { 1009 var hotShell = document.querySelector('.hot-page-shell'); 1010 if (!hotShell) return; 1011 1012 hotShell.querySelectorAll('.hot-page-tab').forEach(function (tab) { 1013 var isActive = tab.getAttribute('data-hot-target') === target; 1014 tab.classList.toggle('active', isActive); 1015 tab.setAttribute('aria-selected', isActive ? 'true' : 'false'); 1016 }); 1017 1018 hotShell.querySelectorAll('.hot-page-panel').forEach(function (panel) { 1019 var isActive = panel.getAttribute('data-hot-panel') === target; 1020 panel.classList.toggle('active', isActive); 1021 }); 1022 1023 queueSyncHeaderColumnHeights(); 1024} 1025 1026function ensureHotPanelSelection() { 1027 var hotShell = document.querySelector('.hot-page-shell'); 1028 if (!hotShell) return; 1029 var activeTab = hotShell.querySelector('.hot-page-tab.active'); 1030 if (activeTab) { 1031 switchHotPanel(activeTab.getAttribute('data-hot-target')); 1032 return; 1033 } 1034 var firstTab = hotShell.querySelector('.hot-page-tab'); 1035 if (firstTab) { 1036 switchHotPanel(firstTab.getAttribute('data-hot-target')); 1037 } 1038} 1039 1040function enhanceArchiveArticleRoot(root) { 1041 if (!root) return; 1042 try { 1043 root.querySelectorAll('a[href]').forEach(function (a) { 1044 var href = a.getAttribute('href') || ''; 1045 if (!href || href.startsWith('#')) return; 1046 a.setAttribute('target', '_blank'); 1047 var rel = (a.getAttribute('rel') || '').toLowerCase();
1048 var relParts = rel.split(/\s+/).filter(Boolean); 1049 if (relParts.indexOf('noopener') === -1) relParts.push('noopener'); 1050 if (relParts.indexOf('noreferrer') === -1) relParts.push('noreferrer'); 1051 a.setAttribute('rel', relParts.join(' ')); 1052 }); 1053 } catch (e) { 1054 } 1055 1056 try { 1057 root.querySelectorAll('pre > code').forEach(function (code) { 1058 var pre = code && code.parentElement ? code.parentElement : null; 1059 if (!pre) return; 1060 if (pre.getAttribute('data-ndh-copy-ready') === '1') return; 1061 pre.setAttribute('data-ndh-copy-ready', '1'); 1062 var btn = document.createElement('button'); 1063 btn.type = 'button'; 1064 btn.className = 'ndh-code-copy'; 1065 btn.setAttribute('aria-label', 'å¤å¶ä»£ç '); 1066 btn.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M16 1H6a2 2 0 0 0-2 2v12h2V3h10V1zm3 4H10a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2zm0 16h-9V7h9v14z"></path></svg>'; 1067 btn.addEventListener('click', function (ev) { 1068 try { 1069 ev.preventDefault(); 1070 ev.stopPropagation(); 1071 } catch (err) { 1072 } 1073 var text = ''; 1074 try { 1075 text = code.textContent || ''; 1076 } catch (err) { 1077 text = ''; 1078 } 1079 function markCopied(ok) { 1080 try { 1081 btn.classList.toggle('copied', !!ok); 1082 clearTimeout(btn._ndhCopyTimer); 1083 btn._ndhCopyTimer = setTimeout(function () { 1084 try { btn.classList.remove('copied'); } catch (e2) {} 1085 }, 1100); 1086 } catch (e2) { 1087 } 1088 } 1089 try { 1090 if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function' && window.isSecureContext) { 1091 navigator.clipboard.writeText(text).then(function () { 1092 markCopied(true); 1093 }).catch(function () { 1094 markCopied(false); 1095 }); 1096 return; 1097 } 1098 } catch (err) { 1099 } 1100 try { 1101 var ta = document.createElement('textarea'); 1102 ta.value = text; 1103 ta.setAttribute('readonly', 'readonly'); 1104 ta.style.position = 'fixed'; 1105 ta.style.opacity = '0'; 1106 ta.style.left = '-9999px'; 1107 document.body.appendChild(ta); 1108 ta.select(); 1109 var ok = false; 1110 try { 1111 ok = document.execCommand('copy'); 1112 } catch (err) { 1113 ok = false; 1114 } 1115 document.body.removeChild(ta); 1116 markCopied(ok); 1117 } catch (err) { 1118 markCopied(false); 1119 } 1120 }, { passive: false }); 1121 pre.appendChild(btn); 1122 }); 1123 } catch (e) { 1124 } 1125} 1126 1127function initArchivePanel() { 1128 var page = document.getElementById('guidang'); 1129 if (!page) return; 1130 var shell = page.querySelector('.archive-page-shell'); 1131 if (!shell) return; 1132 var indexEl = page.querySelector('[data-archive-index]'); 1133 var articleView = page.querySelector('[data-archive-article-view]'); 1134 var titleEl = page.querySelector('[data-archive-article-title]'); 1135 var backBtn = page.querySelector('.archive-back'); 1136 var scrollEl = page.querySelector('.archive-scroll'); 1137 if (!indexEl || !articleView || !scrollEl) return; 1138 var suppressClickUntil = 0; 1139 1140 function markArchiveScrollActive() { 1141 archiveScrollLockUntil = Date.now() + 1400; 1142 } 1143 1144 var touchStartX = 0; 1145 var touchStartY = 0; 1146 var touchTracking = false;
1147 function onTouchStart(e) { 1148 try { 1149 if (!e || !e.touches || e.touches.length !== 1) return; 1150 var t = e.touches[0]; 1151 touchStartX = t.clientX; 1152 touchStartY = t.clientY; 1153 touchTracking = true; 1154 } catch (err) { 1155 } 1156 } 1157 function onTouchEnd() { 1158 touchTracking = false; 1159 } 1160 function onTouchMove(e) { 1161 try { 1162 if (!touchTracking || !e || !e.touches || e.touches.length !== 1) return; 1163 var t = e.touches[0]; 1164 var dx = t.clientX - touchStartX; 1165 var dy = t.clientY - touchStartY; 1166 if (Math.abs(dx) < 6 && Math.abs(dy) < 6) return; 1167 if (Math.abs(dy) >= Math.abs(dx) + 3) { 1168 markArchiveScrollActive(); 1169 } 1170 } catch (err) { 1171 } 1172 } 1173 1174 scrollEl.addEventListener('touchstart', onTouchStart, { passive: true }); 1175 scrollEl.addEventListener('touchmove', onTouchMove, { passive: true }); 1176 scrollEl.addEventListener('touchend', onTouchEnd, { passive: true }); 1177 scrollEl.addEventListener('touchcancel', onTouchEnd, { passive: true }); 1178 scrollEl.addEventListener('wheel', markArchiveScrollActive, { passive: true }); 1179 1180 scrollEl.addEventListener('scroll', function () { 1181 if (Date.now() < archiveScrollProgrammaticUntil) return; 1182 suppressClickUntil = Date.now() + 260; 1183 markArchiveScrollActive(); 1184 }, { passive: true }); 1185 1186 function setView(mode) { 1187 shell.setAttribute('data-archive-view', mode); 1188 } 1189 1190 function clearArticle() { 1191 while (articleView.firstChild) { 1192 articleView.removeChild(articleView.firstChild); 1193 } 1194 if (titleEl) titleEl.textContent = ''; 1195 } 1196 1197 function normalizeRel(href) { 1198 try { 1199 return new URL(href, window.location.href).pathname.replace(/\/+$/, '/') || '/'; 1200 } catch (e) { 1201 return ''; 1202 } 1203 } 1204 1205 var templateById = new Map(); 1206 var templateByPath = new Map(); 1207 page.querySelectorAll('template[data-archive-id]').forEach(function (tpl) { 1208 var id = tpl.getAttribute('data-archive-id') || ''; 1209 if (id) templateById.set(id, tpl); 1210 var url = tpl.getAttribute('data-archive-url') || ''; 1211 if (url) { 1212 var path = normalizeRel(url); 1213 if (path) templateByPath.set(path, tpl); 1214 } 1215 }); 1216 1217 function renderTemplate(tpl) { 1218 if (!tpl) return false; 1219 clearArticle(); 1220 try { 1221 var frag = tpl.content ? tpl.content.cloneNode(true) : null; 1222 if (!frag) return false; 1223 articleView.appendChild(frag); 1224 enhanceArchiveArticleRoot(articleView); 1225 var articleTitle = articleView.querySelector('.archive-article-title'); 1226 if (titleEl && articleTitle) { 1227 titleEl.textContent = articleTitle.textContent || ''; 1228 } 1229 setView('article'); 1230 scrollEl.scrollTop = 0; 1231 return true; 1232 } catch (e) { 1233 return false; 1234 } 1235 } 1236 1237 function openById(id) { 1238 return renderTemplate(templateById.get(id)); 1239 } 1240 1241 function openByHref(href) { 1242 var path = normalizeRel(href); 1243 if (!path) return false; 1244 return renderTemplate(templateByPath.get(path)); 1245 } 1246 1247 page.addEventListener('click', function (e) { 1248 if (Date.now() < suppressClickUntil) { 1249 e.preventDefault(); 1250 return; 1251 } 1252 var link = e.target && e.target.closest ? e.target.closest('a.archive-item[data-archive-id]') : null; 1253 if (!link || !page.contains(link)) return; 1254 var id = link.getAttribute('data-archive-id') || ''; 1255 var href = link.getAttribute('href') || ''; 1256 e.preventDefault(); 1257 if (id && openById(id)) return; 1258 if (href) openByHref(href); 1259 }); 1260 1261 if (backBtn) { 1262 backBtn.addEventListener('click', function () { 1263 clearArticle(); 1264 setView('index'); 1265 scrollEl.scrollTop = 0; 1266 }); 1267 } 1268 1269 articleView.addEventListener('click', function (e) { 1270 var a = e.target && e.target.closest ? e.target.closest('a[href]') : null; 1271 if (!a || !articleView.contains(a)) return; 1272 var href = a.getAttribute('href') || ''; 1273 if (!href || href.startsWith('#')) return; 1274 var target = (a.getAttribute('target') || '').toLowerCase(); 1275 if (target === '_blank') return; 1276 a.setAttribute('target', '_blank'); 1277 }); 1278} 1279 1280document.addEventListener("DOMContentLoaded", function () { 1281 var currentPage = document.querySelector('.page.active'); 1282 syncHeaderPageMode(currentPage ? currentPage.id : 'default'); 1283 ensureHotPanelSelection(); 1284 initArchivePanel(); 1285 window.addEventListener('resize', queueSyncHeaderColumnHeights); 1286 window.addEventListener('load', queueSyncHeaderColumnHeights); 1287 window.addEventListener('resize', observeHeaderColumnChanges); 1288 document.addEventListener('hotlist:updated', function () { 1289 queueSyncHeaderColumnHeights(); 1290 observeHeaderColumnChanges(); 1291 }); 1292 observeHeaderColumnChanges(); 1293}); 1294 1295function broadcastEmbeddedTheme() { 1296 try { 1297 var isNight = document.body && document.body.classList && document.body.classList.contains('io-black-mode'); 1298 document.querySelectorAll('iframe.embedded-page-frame').forEach(function (frame) { 1299 if (!frame || !frame.contentWindow) return; 1300 frame.contentWindow.postMessage({ type: 'ndh:theme', night: isNight ? '1' : '0' }, window.location.origin); 1301 }); 1302 } catch (e) { 1303 } 1304} 1305 1306// Tab 忢åè½
1307function showPage(key) { 1308 // éèææé¡µé¢ 1309 document.querySelectorAll('.page').forEach(function(page) { 1310 page.classList.remove('active'); 1311 }); 1312 // æ¾ç¤ºå½åé¡µé¢ 1313 var current = document.getElementById(key); 1314 if (current) { 1315 current.classList.add('active'); 1316 } 1317 // 忢æé®é«äº® 1318 document.querySelectorAll('.nav-button').forEach(function(btn) { 1319 btn.classList.remove('active'); 1320 }); 1321 // é«äº®å½åæé® 1322 var btns = document.querySelectorAll('.nav-button'); 1323 btns.forEach(function(btn) { 1324 if (btn.getAttribute('onclick') && btn.getAttribute('onclick').includes(key)) { 1325 btn.classList.add('active'); 1326 } 1327 }); 1328 syncHeaderPageMode(key); 1329 if (key === 'waline') { 1330 ensureHotPanelSelection(); 1331 } 1332 broadcastEmbeddedTheme(); 1333 observeHeaderColumnChanges(); 1334}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.