1/** 2 * 主åºç¨é»è¾ 3 * æä»¶: js/app.js 4 * åè½ï¼è¯å ¸æ¥è¯¢ãpaisé«äº®æ»å¨ãé³é¢ææ¾ãå¤å¶ä¿æ¤ 5 */ 6(function() { 7 'use strict'; 8 if (window._appLoaded) return; 9 window._appLoaded = true; 10 11 // ===== å ¨å±åé ===== 12 var lastRequestId = 0, lastSearchTerm = '', currentDocumentPath = '', tocData = []; 13 var suggestionCache = new Map(), searchDebounceTimer = null; 14 var isScrolling = false; 15 16 // ===== éèé³é¢ææ¾å¨ï¼ç¨äºé黿æ¾ï¼===== 17 var hiddenAudioPlayer = null; 18 19// ===== å¨ window.onload 彿°ä¸æ·»å ===== 20window.onload = function() { 21 console.log('页é¢å è½½å®æ'); 22 23 // 馿¬¡å è½½ç«å³æ´æ°ç»è®¡ 24 updateStats(); 25 // å¯å¨éå½å®æ¶å¨ï¼æ¿ä»£ setIntervalï¼ 26 setTimeout(scheduleStatsUpdate, 30000); 27 28 var s = document.getElementById('searchInput'); 29 if (s) { 30 s.addEventListener('input', function(e) { 31 window.changeChar(e); 32 clearTimeout(searchDebounceTimer); 33 searchDebounceTimer = setTimeout(window.getSuggestions, 200); 34 }); 35 s.addEventListener('keydown', function(e) { 36 if (e.key === 'Enter') { e.preventDefault(); window.exactSearch(); } 37 }); 38 } 39 40 var titleEl = document.getElementById('title'); 41 if (titleEl) { 42 titleEl.addEventListener('click', function() { 43 window.loadFooterContent('åè®°.html'); 44 }); 45 } 46 47 if (s) s.value = ''; 48 window.readHtml('åè¨.html'); 49 window.initAudioPlayer(); 50 setTimeout(function() { if (typeof window.addAiSpeakButtons === 'function') window.addAiSpeakButtons(); }, 500); 51 52// ===== PIV2020 å¼¹çªææ½åå§å ===== 53 (function initPivPopupDrag() { 54 var popup = document.getElementById('piv2020Popup'); 55 var header = document.getElementById('piv2020Header'); 56 if (!popup || !header) return; 57 initDrag(popup, header); 58 })(); 59 60 // ===== çè¨æ¿å¼¹çªææ½åå§å ===== 61 (function initGuestbookDrag() { 62 var popup = document.getElementById('guestbookPopup'); 63 var header = document.getElementById('guestbookHeader'); 64 if (!popup || !header) return; 65 initDrag(popup, header); 66 })(); 67 68 // ===== 管çååå°å¼¹çªææ½åå§å ===== 69 (function initAdminDrag() { 70 var popup = document.getElementById('adminPopup'); 71 var header = document.getElementById('adminHeader'); 72 if (!popup || !header) return; 73 initDrag(popup, header); 74 })(); 75 76 // ===== çªå£å¤§å°ååæ¶è°æ´å¼¹çªä½ç½® ===== 77 var resizeTimer = null; 78 window.addEventListener('resize', function() { 79 clearTimeout(resizeTimer); 80 resizeTimer = setTimeout(function() { 81 // å¦ææ¯ææºç«¯ï¼å¼¹çªå·²ç»ç¨ CSS åºå®äºä½ç½®ï¼ä¸éè¦é¢å¤è°æ´ 82 if (window.innerWidth > 768) { 83 // æ¡é¢ç«¯ï¼å¦æå¼¹çªè¶ åºå¯è§åºåï¼è°æ´ä½ç½® 84 ['guestbookPopup', 'adminPopup', 'piv2020Popup'].forEach(function(id) { 85 var popup = document.getElementById(id); 86 if (!popup || popup.style.display !== 'block') return; 87 var rect = popup.getBoundingClientRect(); 88 var maxX = window.innerWidth - 50; 89 var maxY = window.innerHeight - 50; 90 if (rect.right > maxX) { 91 var left = parseInt(popup.style.left) || 0; 92 popup.style.left = Math.max(0, left - (rect.right - maxX)) + 'px'; 93 } 94 if (rect.bottom > maxY) { 95 var top = parseInt(popup.style.top) || 0; 96 popup.style.top = Math.max(0, top - (rect.bottom - maxY)) + 'px'; 97 } 98 }); 99 } 100 }, 300); 101 }); 102 103 // ===== æ°å¢ï¼URL Hash ç´æ¥æ¥è¯¢æ¯æ ===== 104 function handleHashQuery() { 105 var hash = window.location.hash; 106 if (!hash || hash.length <= 1) return; 107 108 var query = decodeURIComponent(hash.substring(1)); 109 // æ¯æ PIV 飿 ¼ç _kd åç¼ 110 if (query.endsWith('_kd')) { 111 query = query.substring(0, query.length - 3); 112 } 113 // å°ä¸åçº¿è½¬ä¸ºç©ºæ ¼ï¼PIV 飿 ¼ï¼ 114 query = query.replace(/_/g, ' '); 115 116 var searchInput = document.getElementById('searchInput'); 117 if (!searchInput) return; 118 119 searchInput.value = query; 120 // 触å input äºä»¶ä»¥æ¾ç¤ºå»ºè®® 121 var event = new Event('input', { bubbles: true });
122 searchInput.dispatchEvent(event); 123 124 // å»¶è¿æ§è¡ç²¾ç¡®æç´¢ 125 setTimeout(function() { 126 window.exactSearch(); 127 }, 350); 128 } 129 130 // 页é¢å è½½åæ§è¡ 131 setTimeout(handleHashQuery, 400); 132 133 // çå¬ hash ååï¼ç¨æ·æå¨ä¿®æ¹ï¼ 134 window.addEventListener('hashchange', function() { 135 var listBox = document.getElementById('listBox'); 136 if (listBox) listBox.innerHTML = ''; 137 setTimeout(handleHashQuery, 100); 138 }); 139}; 140 141// ===== éç¨ææ½å½æ°ï¼æ¯æè§¦æ¸ï¼ ===== 142function initDrag(popup, header) { 143 if (!popup || !header) return; 144 145 var isDragging = false; 146 var startX, startY, startLeft, startTop; 147 148 // å¦ææ¯ææºç«¯ï¼å®½åº¦ < 768pxï¼ï¼ç¦ç¨ææ½ï¼å 为已ç»åºå®ä½ç½®äºï¼ 149 var isMobile = window.innerWidth < 768; 150 151 function dragStart(clientX, clientY) { 152 if (isMobile) return; // ææºç«¯ä¸æå¨ 153 var rect = popup.getBoundingClientRect(); 154 startLeft = rect.left; 155 startTop = rect.top; 156 startX = clientX; 157 startY = clientY; 158 isDragging = true; 159 160 // æå¨æ¶åæ¶ transformï¼æ¹ç¨ left/top å®ä½ 161 popup.style.transform = 'none'; 162 popup.style.left = rect.left + 'px'; 163 popup.style.top = rect.top + 'px'; 164 } 165 166 function dragMove(clientX, clientY) { 167 if (!isDragging) return; 168 var newLeft = startLeft + clientX - startX; 169 var newTop = startTop + clientY - startY; 170 var maxLeft = window.innerWidth - 80; 171 var maxTop = window.innerHeight - 80; 172 popup.style.left = Math.max(-popup.offsetWidth + 60, Math.min(newLeft, maxLeft)) + 'px'; 173 popup.style.top = Math.max(0, Math.min(newTop, maxTop)) + 'px'; 174 } 175 176 function dragEnd() { 177 isDragging = false; 178 } 179 180 // --- é¼ æ äºä»¶ --- 181 header.addEventListener('mousedown', function(e) { 182 if (e.target.tagName === 'SPAN' && e.target.getAttribute('onclick')) return; 183 dragStart(e.clientX, e.clientY); 184 e.preventDefault(); 185 }); 186 187 document.addEventListener('mousemove', function(e) { 188 dragMove(e.clientX, e.clientY); 189 }); 190 191 document.addEventListener('mouseup', dragEnd); 192 193 // --- 触æ¸äºä»¶ --- 194 header.addEventListener('touchstart', function(e) { 195 if (e.target.tagName === 'SPAN' && e.target.getAttribute('onclick')) return; 196 var t = e.touches[0]; 197 dragStart(t.clientX, t.clientY); 198 }, { passive: true }); 199 200 document.addEventListener('touchmove', function(e) { 201 if (!isDragging) return; 202 var t = e.touches[0]; 203 dragMove(t.clientX, t.clientY); 204 e.preventDefault(); 205 }, { passive: false }); 206 207 document.addEventListener('touchend', dragEnd); 208 document.addEventListener('touchcancel', dragEnd); 209} 210 211 212// ===== ç»è®¡æ´æ°ï¼å®å ¨é离é误ï¼ä¸å½±å页é¢ï¼ ===== 213function updateStats() { 214 try { 215 var controller = new AbortController(); 216 var timeoutId = setTimeout(function() { controller.abort(); }, 5000); 217 218 fetch("http://" + window.location.host + "/count?_=" + Date.now(), { 219 signal: controller.signal 220 }) 221 .then(function(r) { 222 clearTimeout(timeoutId); 223 if (!r.ok) return null; 224 return r.json(); 225 }) 226 .then(function(d) { 227 var logEl = document.getElementById('log'); 228 if (logEl) { 229 var line1 = 'ç½é¡µè®¿é®ç»è®¡ï¼ä»æ¥' + (d.today || 0) + 230 ', æ¨æ¥' + (d.yestoday || 0) + 231 ', æ¬å¨' + (d.week || 0) + 232 ', æ¬æ' + (d.month || 0) + 233 ', æ¬å¹´' + (d.year || 0) + 234 ', å ¨é¨' + (d.all || 0); 235 236 var line2 = 'çå®è®¿é®ç»è®¡ï¼å®æ¶å¨çº¿' + (d.online || 0) + 237 ', 仿¥è®¿é®' + (d.todayuv || 0) + 238 ', çå®è®¿é®å计(2026-08-05å¼å§)<span id="alltimeBtn" style="color:#006400;text-decoration:underline;cursor:pointer;font-weight:bold;" title="ç¹å»æ¥çå°åºåå¸">' + (d.alltime || 0) + '</span>'; 239 240 logEl.innerHTML = line1 + '<br>' + line2; 241 242 // ç»å®ç¹å»äºä»¶ 243 setTimeout(function() { 244 var btn = document.getElementById('alltimeBtn'); 245 if (btn) btn.onclick = showProvincePopup; 246 }, 0); 247 } 248 }) 249 .catch(function() { 250 // å®å ¨éé»ï¼ä¸ console.errorï¼ä¸æå¼å¸¸ï¼é¿å 触åå ¨å±é误çå¬ 251 }); 252 } catch (e) { 253 // å®å ¨éé» 254 } 255} 256 257// ä½¿ç¨ setTimeout éå½ï¼åªå¨é¡µé¢å¯è§æ¶æ´æ°ï¼åå°æ ç¾é¡µæå 258function scheduleStatsUpdate() { 259 if (document.visibilityState === 'visible') { 260 updateStats(); 261 } 262 setTimeout(scheduleStatsUpdate, 30000); 263} 264 265 // ==================== çå¾ å ç´ åºç°çè¾ å©å½æ° ==================== 266 function waitForElement(selector, timeout, callback) { 267 var startTime = Date.now(); 268 var interval = setInterval(function() { 269 var element = document.querySelector(selector); 270 if (element) { 271 clearInterval(interval); 272 callback(element); 273 } else if (Date.now() - startTime > timeout) { 274 clearInterval(interval); 275 console.log('çå¾ å ç´ è¶ æ¶:', selector); 276 callback(null); 277 } 278 }, 50); 279 } 280 281 function waitForElementInWebBrowser(selector, timeout, callback) { 282 var webBrowser = document.getElementById('webBrowser'); 283 if (!webBrowser) { 284 callback(null); 285 return; 286 } 287 var startTime = Date.now(); 288 var interval = setInterval(function() { 289 var element = webBrowser.querySelector(selector); 290 if (element) { 291 clearInterval(interval); 292 callback(element); 293 } else if (Date.now() - startTime > timeout) { 294 clearInterval(interval); 295 console.log('çå¾ å ç´ è¶ æ¶:', selector); 296 callback(null); 297 } 298 }, 50); 299 } 300 301 // ==================== paisé«äº®æ»å¨å½æ° ==================== 302 303 function getPaisElements() { 304 var webBrowser = document.getElementById('webBrowser'); 305 if (!webBrowser) return []; 306 var elements = webBrowser.querySelectorAll('strong.pais, .pais, [class*="pais"]'); 307 return elements; 308 } 309 310 window.scrollToSpecificPais = function(paisContent, searchTerm) { 311 console.log('scrollToSpecificPais è°ç¨, paisContent:', paisContent, 'searchTerm:', searchTerm); 312 313 waitForElementInWebBrowser('strong.pais, .pais, [class*="pais"]', 3000, function(paisElementsList) { 314 if (!paisElementsList) return; 315 316 var webBrowser = document.getElementById('webBrowser'); 317 if (!webBrowser) return; 318 319 var paisElements = webBrowser.querySelectorAll('strong.pais, .pais, [class*="pais"]'); 320 if (!paisElements.length) return; 321 322 var targetElement = null; 323 var normalizedContent = paisContent.trim(); 324 325 for (var i = 0; i < paisElements.length; i++) { 326 var text = paisElements[i].textContent.trim(); 327 if (text === normalizedContent) { 328 targetElement = paisElements[i]; 329 break; 330 } 331 } 332 333 if (!targetElement) { 334 var cleanContent = normalizedContent.replace(/^\*/, '').trim(); 335 for (var i = 0; i < paisElements.length; i++) { 336 var text = paisElements[i].textContent.trim(); 337 if (text === cleanContent || text.indexOf(cleanContent) !== -1) { 338 targetElement = paisElements[i]; 339 break; 340 } 341 } 342 } 343 344 if (!targetElement && searchTerm && searchTerm.length > 1) { 345 for (var i = 0; i < paisElements.length; i++) { 346 var text = paisElements[i].textContent; 347 if (text.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1) { 348 targetElement = paisElements[i]; 349 break; 350 } 351 } 352 } 353 354 if (targetElement) { 355 for (var i = 0; i < paisElements.length; i++) { 356 var html = paisElements[i].innerHTML; 357 if (html.indexOf('#FF00FF') !== -1) { 358 paisElements[i].innerHTML = paisElements[i].textContent; 359 } 360 } 361 362 if (searchTerm && searchTerm.length > 1) { 363 var content = targetElement.textContent; 364 var termIndex = content.toLowerCase().indexOf(searchTerm.toLowerCase()); 365 if (termIndex >= 0) { 366 var before = content.substring(0, termIndex); 367 var match = content.substring(termIndex, termIndex + searchTerm.length); 368 var after = content.substring(termIndex + searchTerm.length); 369 targetElement.innerHTML = before + '<span style="color:#FF00FF;background-color:rgba(255,0,255,0.2);">' + match + '</span>' + after; 370 } 371 } 372 373 targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); 374 } 375 }); 376 }; 377 378 window.scrollToFirstPais = function() { 379 console.log('scrollToFirstPais è°ç¨'); 380 381 waitForElementInWebBrowser('strong.pais, .pais, [class*="pais"]', 3000, function() { 382 var webBrowser = document.getElementById('webBrowser'); 383 if (!webBrowser) return; 384 385 var paisElements = webBrowser.querySelectorAll('strong.pais, .pais, [class*="pais"]'); 386 if (!paisElements.length) return; 387 388 var target = null; 389 for (var i = 0; i < paisElements.length; i++) { 390 if (paisElements[i].querySelector('span[style*="#FF00FF"]')) { 391 target = paisElements[i]; 392 break; 393 } 394 } 395 396 var scrollTarget = target || paisElements[0]; 397 scrollTarget.scrollIntoView({ behavior: 'smooth', block: 'start' }); 398 }); 399 }; 400 401 window.ensureScrollToPais = function() { 402 if (isScrolling) return; 403 isScrolling = true; 404 window.scrollToFirstPais(); 405 setTimeout(function() { isScrolling = false; }, 800); 406 }; 407 408 window.scrollToKapvorto = function() { 409 console.log('scrollToKapvorto è°ç¨'); 410 411 waitForElementInWebBrowser('.kapvorto, [class*="kapvorto"]', 3000, function() { 412 var webBrowser = document.getElementById('webBrowser'); 413 if (!webBrowser) return; 414 415 var kapvortoElement = webBrowser.querySelector('.kapvorto, [class*="kapvorto"]'); 416 if (kapvortoElement) { 417 kapvortoElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); 418 return; 419 } 420 421 var firstElement = webBrowser.querySelector('p, div, .entry-body'); 422 if (firstElement) { 423 firstElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); 424 } else { 425 webBrowser.scrollIntoView({ behavior: 'smooth', block: 'start' }); 426 } 427 }); 428 }; 429 430 // ==================== æ¾ç¤ºå®ä¹å½æ° ==================== 431 432 window.displayDefinition = function(item) { 433 var w = document.getElementById('webBrowser'); 434 if (!w) return; 435 436 var cleanWord = ''; 437 if (item.word) { 438 cleanWord = item.word.replace(/[¹²³â´âµâ¶â·â¸â¹â°]/g, '').replace(/â³/g, '').trim(); 439 } 440 441 var html = ''; 442 html += '<p>' + (item.definition || '') + '</p>'; 443 w.innerHTML = html; 444 445 // å¨ kapvorto å pais èç¹åæ·»å PIV2020 龿¥ 446 addPivLinksToDefinition(); 447 448 setTimeout(function() { 449 if (typeof window.addParagraphIdentifiers === 'function') { 450 window.addParagraphIdentifiers(); 451 } 452 if (typeof window.processDefinitionAudioLinks === 'function') { 453 window.processDefinitionAudioLinks(); 454 } 455 if (typeof window.addAiSpeakButtons === 'function') { 456 window.addAiSpeakButtons(); 457 } 458 459 var searchInput = document.getElementById('searchInput'); 460 if (searchInput) { 461 var searchTerm = searchInput.value.trim().toLowerCase(); 462 var wordLower = (item.word || '').toLowerCase(); 463 464 if (item.word && item.word.indexOf('â³') !== -1) { 465 var paisContent = item.word.replace('â³', '').trim(); 466 window.scrollToSpecificPais(paisContent, searchTerm); 467 } 468 else if (wordLower === searchTerm || (searchTerm && wordLower.indexOf(searchTerm) === 0)) { 469 window.scrollToKapvorto(); 470 } 471 else { 472 window.scrollToFirstPais(); 473 } 474 } 475 }, 200); 476 };; 477 478 // ==================== æ¾ç¤ºå»ºè®®å½æ° ==================== 479 480 function displaySuggestionsOptimized(suggestions, searchTerm) { 481 var currentInput = document.getElementById('searchInput'); 482 if (!currentInput || currentInput.value.trim() !== searchTerm) return; 483 484 var lb = document.getElementById('listBox'); 485 if (!lb) return; 486 var fragment = document.createDocumentFragment(); 487 488 var wordMatches = suggestions.filter(function(item) { 489 return item.word && item.word.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1; 490 }); 491 492 var itemsToShow = wordMatches.length > 0 ? wordMatches : suggestions; 493 var displayItems = itemsToShow.slice(0, 100); 494
495 displayItems.forEach(function(item, idx) { 496 var div = document.createElement('div'); 497 div.style.cssText = 'margin-bottom:1px;cursor:pointer;padding:1px;white-space:nowrap;'; 498 var wordDiv = document.createElement('div'); 499 wordDiv.textContent = item.word; 500 wordDiv.style.cssText = 'white-space:pre;font-family:monospace;'; 501 div.appendChild(wordDiv); 502 503 var isPaisEntry = item.word && item.word.indexOf('â³') !== -1; 504 505 div.addEventListener('click', function() { 506 var items = lb.getElementsByClassName('highlight'); 507 while(items.length > 0) items[0].classList.remove('highlight'); 508 div.classList.add('highlight'); 509 window.displayDefinition(item); 510 511 if (isPaisEntry) { 512 var paisContent = item.word.replace('â³', '').trim(); 513 window.scrollToSpecificPais(paisContent, searchTerm); 514 } 515 }); 516 fragment.appendChild(div); 517 }); 518 519 lb.innerHTML = ''; 520 lb.appendChild(fragment); 521 522 if (displayItems.length > 0 && lb.children[0]) { 523 lb.children[0].classList.add('highlight'); 524 setTimeout(function() { 525 window.displayDefinition(displayItems[0]); 526 }, 0); 527 } 528 } 529 530 // ==================== é³é¢ææ¾æ ¸å¿å½æ°ï¼å®å ¨æç §åå§ä»£ç æ¢å¤ï¼==================== 531 532 /** 533 * åå§åé³é¢ææ¾å¨ 534 */ 535 window.initAudioPlayer = function() { 536 // å建éèé³é¢ææ¾å¨ 537 if (!document.getElementById('hiddenAudioPlayer')) { 538 var ha = document.createElement('audio'); 539 ha.id = 'hiddenAudioPlayer'; 540 ha.style.display = 'none'; 541 document.body.appendChild(ha); 542 } 543 hiddenAudioPlayer = document.getElementById('hiddenAudioPlayer'); 544 545 // å建å¯è§é³é¢ææ¾å¨å®¹å¨ 546 if (!document.getElementById('audioPlayerContainer')) { 547 var container = document.createElement('div'); 548 container.id = 'audioPlayerContainer'; 549 container.className = 'audio-player-container hidden'; 550 container.innerHTML = '<div class="audio-player"><audio id="hmrbPlayer" controls></audio><button class="close-audio-btn" onclick="window.closeAudioPlayer()">\u00D7</button></div>'; 551 document.body.appendChild(container); 552 } 553 554 // å ¨å±ç¹å»äºä»¶ï¼å¤çMP3龿¥ 555 document.addEventListener('click', function(e) { 556 var target = e.target; 557 if (target.tagName === 'A') { 558 var href = target.getAttribute('href'); 559 if (href && href.toLowerCase().endsWith('.mp3')) { 560 e.preventDefault(); 561 var wb = document.getElementById('webBrowser'); 562 if (wb && wb.contains(target)) { 563 window.playAudioWithControls(href); 564 } else { 565 window.playAudioWithoutControls(href); 566 } 567 } 568 } 569 }); 570 571 console.log('é³é¢ææ¾å¨åå§å宿'); 572 }; 573 574 /** 575 * ææ¾é³é¢ï¼æ¾ç¤ºææ¾æ§ä»¶ï¼ 576 */ 577 window.playAudioWithControls = function(audioUrl) { 578 var player = document.getElementById('hmrbPlayer'); 579 var container = document.getElementById('audioPlayerContainer'); 580 if (!player || !container) return; 581 582 // å¤çç¸å¯¹è·¯å¾ 583 var url = audioUrl; 584 if (!url.includes('/') && !url.startsWith('http')) { 585 url = 'Sonoj/' + url; 586 } 587 588 player.src = url; 589 container.classList.remove('hidden'); 590 player.controls = true; 591 player.play().catch(function() {}); 592 }; 593 594 /** 595 * ææ¾é³é¢ï¼éèæ§ä»¶ï¼ 596 */ 597 window.playAudioWithoutControls = function(audioUrl) { 598 if (!hiddenAudioPlayer) { 599 var ha = document.createElement('audio'); 600 ha.id = 'hiddenAudioPlayer'; 601 ha.style.display = 'none'; 602 document.body.appendChild(ha); 603 hiddenAudioPlayer = ha; 604 } 605 606 // å¤çç¸å¯¹è·¯å¾ 607 var url = audioUrl; 608 if (!url.includes('/') && !url.startsWith('http')) { 609 url = 'Sonoj/' + url; 610 } 611 612 hiddenAudioPlayer.src = url; 613 hiddenAudioPlayer.play().catch(function() {}); 614 }; 615 616 /** 617 * å ³éé³é¢ææ¾å¨ 618 */ 619 window.closeAudioPlayer = function() { 620 var player = document.getElementById('hmrbPlayer'); 621 var container = document.getElementById('audioPlayerContainer'); 622 if (player) { 623 player.pause(); 624 player.src = ''; 625 } 626 if (container) { 627 container.classList.add('hidden'); 628 } 629 }; 630 631 /** 632 * åå»ºææ¾å¾æ 633 */ 634 window.createAudioPlayIcon = function() { 635 var img = document.createElement('img'); 636 img.src = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iI2Q0MGEwMCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48cGF0aCBkPSJNMTAgOHY4bDYuNzYtNHoiIGZpbGw9InVybCgjZ3JhZGllbnQpIi8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudCIgeDE9IjAiIHkxPSIwIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNlZDM1MzUiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmNiMDciLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4='; 637 img.width = 18; 638 img.height = 18; 639 img.style.verticalAlign = 'middle'; 640 img.alt = 'ææ¾'; 641 return img; 642 }; 643 644 /** 645 * å¤çHTMLææ¡£ä¸çMP3龿¥ï¼LoadEsperantoFileå è½½çå å®¹ï¼ 646 */ 647 window.processHtmlAudioLinks = function() { 648 var w = document.getElementById('webBrowser'); 649 if (!w) return; 650 651 var audioLinks = w.querySelectorAll('a[href$=".mp3"], a[href$=".MP3"]'); 652 console.log('HTMLææ¡£ä¸æ¾å°é³é¢é¾æ¥:', audioLinks.length); 653 654 audioLinks.forEach(function(link) { 655 // 妿龿¥å å·²æåå ç´ ï¼åªæ·»å ç¹å»äºä»¶ 656 if (link.children.length > 0) { 657 link.style.cursor = 'pointer'; 658 if (!link.title) link.title = 'ç¹å»ææ¾æ¬å°é³é¢'; 659 // ç§»é¤åæäºä»¶ï¼éæ°ç»å® 660 var oldClick = link.onclick; 661 link.onclick = null; 662 link.addEventListener('click', function(e) { 663 e.preventDefault(); 664 var href = this.getAttribute('href'); 665 if (href) { 666 window.playAudioWithControls(href); 667 } 668 }); 669 return; 670 } 671 672 // ç©ºé¾æ¥ï¼æ·»å 徿 åæ ·å¼ 673 link.title = 'ç¹å»ææ¾æ¬å°é³é¢'; 674 link.style.cursor = 'pointer'; 675 link.style.color = '#006400';
676 link.style.textDecoration = 'none'; 677 link.style.borderBottom = '1px dotted #006400'; 678 link.style.margin = '0 2px'; 679 link.style.display = 'inline-flex'; 680 link.style.alignItems = 'center'; 681 682 var textContent = link.textContent.trim(); 683 if (!textContent || textContent === link.getAttribute('href') || textContent === '') { 684 link.innerHTML = ''; 685 var icon = window.createAudioPlayIcon(); 686 link.appendChild(icon); 687 } 688 689 link.addEventListener('mouseover', function() { 690 this.style.color = '#ff6600'; 691 this.style.borderBottomColor = '#ff6600'; 692 }); 693 694 link.addEventListener('mouseout', function() { 695 this.style.color = '#006400'; 696 this.style.borderBottomColor = '#006400'; 697 }); 698 699 link.addEventListener('click', function(e) { 700 e.preventDefault(); 701 var href = this.getAttribute('href'); 702 if (href) { 703 window.playAudioWithControls(href); 704 } 705 }); 706 }); 707 }; 708 709 /** 710 * å¤çè¯å ¸æ¥è¯¢ç»æä¸çMP3龿¥ 711 */ 712 window.processDefinitionAudioLinks = function() { 713 var w = document.getElementById('webBrowser'); 714 if (!w) return; 715 716 w.querySelectorAll('a[href$=".mp3"]').forEach(function(link) { 717 // 妿已æåå ç´ ï¼è·³è¿ 718 if (link.children.length > 0) return; 719 720 var audioHref = link.getAttribute('href'); 721 if (!audioHref.startsWith('http') && !audioHref.startsWith('/')) { 722 if (!audioHref.startsWith('Sonoj/')) audioHref = 'Sonoj/' + audioHref; 723 } 724 725 link.innerHTML = ''; 726 link.style.cssText = 'cursor:pointer;text-decoration:none;display:inline-block;vertical-align:middle;margin:0 3px;'; 727 link.title = 'ç¹å»ææ¾æ¬å°é³é¢'; 728 729 var icon = window.createAudioPlayIcon(); 730 link.appendChild(icon); 731 732 link.onclick = function(e) { 733 e.preventDefault(); 734 window.playAudioWithoutControls(audioHref); 735 }; 736 }); 737 }; 738 739 // ==================== æ¾ç¤ºå 容彿° ==================== 740 741 window.displayInWebBrowser = function(content) { 742 var w = document.getElementById('webBrowser'); 743 if (!w) return; 744 745 w.querySelectorAll('.ai-speak-btn').forEach(function(e) { e.remove(); }); 746 if (typeof window.resetAiButton === 'function') window.resetAiButton(); 747 w.innerHTML = content; 748 749 setTimeout(function() { 750 if (typeof window.addParagraphIdentifiers === 'function') { 751 window.addParagraphIdentifiers(); 752 } 753 window.processHtmlAudioLinks(); 754 if (typeof window.addAiSpeakButtons === 'function') { 755 window.addAiSpeakButtons(); 756 } 757 }, 50); 758 759 setTimeout(function() { 760 if (typeof window.addAiSpeakButtons === 'function') { 761 window.addAiSpeakButtons(); 762 } 763 window.onDocumentLoaded(); 764 }, 300); 765 }; 766 767 // ==================== ç®å½ç¸å ³å½æ° ==================== 768 769 window.LoadEsperantoFile = function(fileName) { 770 window.clearPreviousResults(); 771 currentDocumentPath = fileName; 772 fetch(fileName).then(function(r) { 773 if (!r.ok) { window.showMessage('æä»¶ä¸åå¨!'); return; } 774 return r.text(); 775 }).then(function(c) { 776 if (c) { window.displayInWebBrowser(c); window.generateHtmlTOC(c); } 777 }).catch(function(e) { window.showMessage('å è½½æä»¶æ¶åºé: ' + e.message); }); 778 }; 779 780 window.loadFooterContent = function(fileName) { window.clearInputs(); window.readHtml(fileName); }; 781 window.vortoremoru = function(fileName) { window.clearInputs(); window.readHtml(fileName); }; 782 783 window.readHtml = function(fileName) { 784 var w = document.getElementById('webBrowser'); 785 fetch(fileName).then(function(r) { 786 if (!r.ok) throw new Error('ç½ç»ååºä¸å¯ç¨'); 787 return r.text(); 788 }).then(function(html) { 789 w.innerHTML = ''; 790 var t = document.createElement('div'); 791 t.innerHTML = html; 792 var scripts = t.getElementsByTagName('script'); 793 for (var i = 0; i < t.childNodes.length; i++) { 794 if (t.childNodes[i].nodeName !== 'SCRIPT') w.appendChild(t.childNodes[i].cloneNode(true)); 795 } 796 for (var j = 0; j < scripts.length; j++) { 797 if (scripts[j].src) { 798 var ns = document.createElement('script'); 799 ns.src = scripts[j].src; 800 document.head.appendChild(ns); 801 } else { 802 try { (new Function(scripts[j].innerHTML)).call(window); } catch(e) {} 803 } 804 } 805 setTimeout(function() { 806 window.processHtmlAudioLinks(); 807 if (typeof window.addAiSpeakButtons === 'function') window.addAiSpeakButtons(); 808 }, 100); 809 }).catch(function() { w.innerHTML = '<p class="error">å è½½å 容失败</p>'; });
810 }; 811 812 window.readText = function(fileName) { 813 fetch('http://' + window.location.host + '/readText/' + fileName) 814 .then(function(r) { return r.text(); }) 815 .then(function(t) { 816 document.getElementById('webBrowser').innerHTML = t; 817 setTimeout(function() { 818 window.processHtmlAudioLinks(); 819 if (typeof window.addAiSpeakButtons === 'function') window.addAiSpeakButtons(); 820 }, 100); 821 }).catch(function() {}); 822 }; 823 824 window.clearInputs = function() { 825 var s = document.getElementById('searchInput'), l = document.getElementById('listBox'); 826 if (s) s.value = ''; if (l) l.innerHTML = ''; 827 }; 828 829 window.clearPreviousResults = function() { 830 var w = document.getElementById('webBrowser'), l = document.getElementById('listBox'); 831 if (w) w.innerHTML = ''; if (l) l.innerHTML = ''; 832 tocData = []; 833 }; 834 835 window.generateHtmlTOC = function(htmlContent) { 836 var t = document.createElement('div'); 837 t.innerHTML = htmlContent; 838 var lb = document.getElementById('listBox'); 839 lb.innerHTML = ''; 840 tocData = []; 841 842 // ç¨äºè®°å½æ¯ç§æ é¢ç±»åï¼tagName + textï¼çåºç°æ¬¡æ° 843 var headerTypeCounters = {}; 844 845 t.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(function(h, i) { 846 var ht = window.decodeHTMLEntities(h.textContent.trim()); 847 if (!ht) return; 848 var lv = parseInt(h.tagName.substring(1)); 849 850 // ä½¿ç¨æ é¢ç±»å+ææ¬ä½ä¸ºé®æ¥è®¡æ° 851 var headerKey = h.tagName.toLowerCase() + ':' + ht; 852 var headerIndex = headerTypeCounters[headerKey] || 0; 853 headerTypeCounters[headerKey] = headerIndex + 1; 854 855 var li = document.createElement('li'); 856 li.style.paddingLeft = ((lv - 1) * 20) + 'px'; 857 li.textContent = ht; 858 li.dataset.index = i; 859 li.dataset.level = lv; 860 861 // ä¿å宿´çå®ä½ä¿¡æ¯ 862 tocData.push({ 863 text: ht, 864 element: h.cloneNode(true), 865 level: lv, 866 index: i, 867 tagName: h.tagName.toLowerCase(), 868 headerIndex: headerIndex 869 }); 870 871 // ç¹å»æ¶ä¼ æ´ä¸ªtocData项 872 li.addEventListener('click', function() { 873 window.showContent(tocData[i]); 874 window.highlightTOCItem(i); 875 }); 876 877 lb.appendChild(li); 878 }); 879 880 if (!lb.querySelector('ul') && lb.children.length > 0) { 881 var ul = document.createElement('ul'); 882 while (lb.firstChild) ul.appendChild(lb.firstChild); 883 lb.appendChild(ul); 884 } 885 if (lb.querySelector('li')) window.highlightTOCItem(0); 886 }; 887 888 window.highlightTOCItem = function(index) { 889 var items = document.querySelectorAll('#listBox li'); 890 items.forEach(function(i) { i.classList.remove('highlight'); }); 891 if (items[index]) items[index].classList.add('highlight'); 892 }; 893 894 window.showContentByText = function(headerText, tagName, headerIndex) { 895 var w = document.getElementById('webBrowser'); 896 if (!w) return; 897 898 // å¦ææ²¡ææä¾tagNameåheaderIndexï¼éåä¸ºåæ¥çè¡ä¸º 899 if (!tagName) { 900 var headers = w.querySelectorAll('h1,h2,h3,h4,h5,h6'); 901 var target = null; 902 for (var i = 0; i < headers.length; i++) { 903 if (headers[i].textContent.trim() === headerText) { 904 target = headers[i]; 905 break; 906 } 907 } 908 if (target) { 909 target.scrollIntoView({ behavior: 'smooth', block: 'start' }); 910 var bg = target.style.backgroundColor; 911 target.style.backgroundColor = 'rgba(255,255,0,0.3)'; 912 setTimeout(function() { target.style.backgroundColor = bg; }, 2000); 913 } 914 return; 915 } 916 917 // éè¿tagNameçé + headerIndex顺åºå¹é ï¼ç²¾ç¡®å®ä½ 918 var headers = w.querySelectorAll(tagName); 919 var target = null; 920 var matchIndex = 0; 921 922 for (var i = 0; i < headers.length; i++) { 923 if (headers[i].textContent.trim() === headerText) { 924 if (matchIndex === headerIndex) { 925 target = headers[i];
926 break; 927 } 928 matchIndex++; 929 } 930 } 931 932 if (target) { 933 target.scrollIntoView({ behavior: 'smooth', block: 'start' }); 934 var bg = target.style.backgroundColor; 935 target.style.backgroundColor = 'rgba(255,255,0,0.3)'; 936 setTimeout(function() { target.style.backgroundColor = bg; }, 2000); 937 } 938 }; 939 940 window.showContent = function(tocItem) { 941 if (!tocItem) return; 942 window.showContentByText(tocItem.text, tocItem.tagName, tocItem.headerIndex); 943 }; 944 945 window.onDocumentLoaded = function() { 946 if (tocData.length > 0 && tocData[0]) { 947 setTimeout(function() { 948 window.showContent(tocData[0]); 949 }, 100); 950 } 951 }; 952 953 window.decodeHTMLEntities = function(text) { 954 var t = document.createElement('textarea'); 955 t.innerHTML = text; 956 return t.value; 957 }; 958 959 // ==================== å¤å¶ä¿æ¤æ®µè½æ è¯ ==================== 960 961 window.addParagraphIdentifiers = function() { 962 var w = document.getElementById('webBrowser'); 963 if (!w) return 0; 964 965 var existingCount = w.querySelectorAll('[data-para-id]').length; 966 var newCount = 0; 967 968 var elements = w.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, li, td, .ckz, .pais, .kapvorto, .sekciokapo-subgrupo'); 969 970 for (var i = 0; i < elements.length; i++) { 971 var el = elements[i]; 972 var text = el.textContent || el.innerText; 973 if (text && text.trim().length > 0 && !el.hasAttribute('data-para-id')) { 974 el.setAttribute('data-para-id', 'para_' + (existingCount + newCount++)); 975 } 976 } 977 978 return existingCount + newCount; 979 }; 980 981 // ==================== å·¥å ·å½æ° ==================== 982 983 window.changeChar = function(event) { 984 var input = event.target.value, result = input; 985 var map = { cx:'Ä', gx:'Ä', hx:'Ä¥', jx:'ĵ', sx:'Å', ux:'Å', cw:'Ä', gw:'Ä', hw:'Ĥ', jw:'Ä´', sw:'Å', uw:'Ŭ' }; 986 for (var k in map) { 987 if (input.indexOf(k) >= 0) result = result.split(k).join(map[k]); 988 } 989 if (result !== input) { 990 document.getElementById('searchInput').value = result; 991 event.target.setSelectionRange(result.length, result.length); 992 } 993 }; 994 995 window.getKeyWord = function() { 996 var s = document.getElementById('searchInput'); 997 return s ? s.value.toLowerCase() : ''; 998 }; 999 1000 window.showMessage = function(content) { 1001 var m = document.getElementById('message'), c = document.querySelector('.message'); 1002 if (!m || !c) return; 1003 m.textContent = content; 1004 c.classList.add('show'); 1005 setTimeout(function() { c.classList.remove('show'); }, 3000); 1006 }; 1007 1008 window.closeMessage = function() { 1009 var c = document.querySelector('.message'); 1010 if (c) c.classList.remove('show'); 1011 }; 1012 1013 window.setWebBrowser = function(str) { 1014 var w = document.getElementById('webBrowser'); 1015 if (w) w.innerHTML = str; 1016 }; 1017 1018 window.showPopup = function() { 1019 var p = document.getElementById('itemPopup'); 1020 if (p) p.style.display = 'block'; 1021 }; 1022 1023 window.closePopup = function() { 1024 var p = document.getElementById('itemPopup'); 1025 if (p) p.style.display = 'none'; 1026 }; 1027 1028 // ==================== æç´¢ç¸å ³å½æ° ==================== 1029 1030 window.getSuggestions = function() { 1031 var input = document.getElementById('searchInput'); 1032 if (!input) return; 1033 var val = input.value.trim(); 1034 lastSearchTerm = val; 1035 if (!val) { 1036 var lb = document.getElementById('listBox'); 1037 if (lb) lb.innerHTML = ''; 1038 return; 1039 } 1040 clearTimeout(searchDebounceTimer); 1041 searchDebounceTimer = setTimeout(function() { getSuggestionsInternal(val); }, 200); 1042 }; 1043 1044 function getSuggestionsInternal(input) { 1045 var currentInput = document.getElementById('searchInput'); 1046 if (!currentInput || currentInput.value.trim() !== input) return; 1047 1048 var cacheKey = input.toLowerCase(); 1049 if (suggestionCache.has(cacheKey)) { 1050 displaySuggestionsOptimized(suggestionCache.get(cacheKey), input); 1051 return; 1052 } 1053
1054 var rid = ++lastRequestId; 1055 fetchSuggestionsWithTimeout(input, rid).then(function(s) { 1056 if (rid !== lastRequestId) return; 1057 if (!currentInput || currentInput.value.trim() !== input) return; 1058 1059 if (!Array.isArray(s) || s.length === 0) { 1060 var pi = processInputQuery(input); 1061 if (pi !== input.toLowerCase()) { 1062 fetchSuggestionsWithTimeout(pi, rid).then(function(s2) { 1063 if (rid !== lastRequestId) return; 1064 if (Array.isArray(s2) && s2.length > 0) { 1065 suggestionCache.set(cacheKey, s2); 1066 displaySuggestionsOptimized(s2, input); 1067 } else { 1068 var lb = document.getElementById('listBox'); 1069 if (lb) lb.innerHTML = ''; 1070 } 1071 }); 1072 return; 1073 } 1074 } 1075 1076 if (Array.isArray(s) && s.length > 0) { 1077 suggestionCache.set(cacheKey, s); 1078 if (suggestionCache.size > 100) suggestionCache.delete(suggestionCache.keys().next().value); 1079 displaySuggestionsOptimized(s, input); 1080 } else { 1081 var lb = document.getElementById('listBox'); 1082 if (lb) lb.innerHTML = ''; 1083 } 1084 }); 1085 } 1086 1087 function fetchSuggestionsWithTimeout(term, requestId) { 1088 return new Promise(function(resolve) { 1089 var controller = new AbortController(); 1090 var timeoutId = setTimeout(function() { controller.abort(); resolve([]); }, 3000); 1091 fetch('http://' + window.location.host + '/getSuggestions/' + encodeURIComponent(term), { signal: controller.signal }) 1092 .then(function(r) { 1093 clearTimeout(timeoutId); 1094 if (requestId === lastRequestId) return r.json(); 1095 throw new Error('expired'); 1096 }) 1097 .then(function(d) { resolve(d); }) 1098 .catch(function() { clearTimeout(timeoutId); resolve([]); }); 1099 }); 1100 } 1101 1102 function processInputQuery(q) { 1103 var ignoreSuffixes = ['jn', 'j', 'n']; 1104 var replaceSuffixes = ['anta','ante','anto','inta','inte','into','ata','ate','ato','ita','ite','ito','ota','ote','oto','as','is','os','us','u']; 1105 function removeSuffix(word, suffixes) { 1106 for (var i = 0; i < suffixes.length; i++) { 1107 if (word.endsWith(suffixes[i])) return word.slice(0, -suffixes[i].length); 1108 } 1109 return word; 1110 } 1111 var w = removeSuffix(q.toLowerCase(), ignoreSuffixes); 1112 if (replaceSuffixes.indexOf(w) === -1) { 1113 for (var i = 0; i < replaceSuffixes.length; i++) { 1114 if (w.endsWith(replaceSuffixes[i])) { 1115 w = w.slice(0, -replaceSuffixes[i].length) + 'i'; 1116 break; 1117 } 1118 } 1119 } 1120 return w; 1121 } 1122 1123 window.exactSearch = function() { 1124 var term = window.getKeyWord().trim(); 1125 if (!term) return; 1126 var regex = /[a-zA-ZÄÄÄÄĥĤĵĴÅÅÅŬ\u4e00-\u9fa5]/; 1127 if (!regex.test(term)) { 1128 window.showMessage('请è¾å ¥ä¸çè¯åè¯æä¸æè¯æ±ï¼'); 1129 return; 1130 } 1131 tryExactSearch(term).then(function(r) { 1132 if (r) return; 1133 var pt = processInputTerm(term); 1134 if (pt !== term.toLowerCase()) { 1135 tryExactSearch(pt).then(function(r2) { 1136 if (!r2) window.setWebBrowser(term + ' è¯åºä¸ææ '); 1137 }); 1138 } else { 1139 window.setWebBrowser(term + ' è¯åºä¸ææ '); 1140 } 1141 }); 1142 }; 1143 1144 function tryExactSearch(term) { 1145 return window.callApi('exactSearch/' + encodeURIComponent(term)).then(function(r) { return r !== '2'; }); 1146 } 1147 1148 function processInputTerm(term) { 1149 var ignoreSuffixes = ['jn', 'j', 'n']; 1150 var replaceSuffixes = ['anta','ante','anto','inta','inte','into','onta','onte','onto','ata','ate','ato','ita','
1150ite','ito','ota','ote','oto','as','is','os','us','u']; 1151 var w = term.toLowerCase(); 1152 for (var i = 0; i < ignoreSuffixes.length; i++) { 1153 if (w.endsWith(ignoreSuffixes[i])) { 1154 w = w.slice(0, -ignoreSuffixes[i].length); 1155 break; 1156 } 1157 } 1158 if (w.length >= 3 && replaceSuffixes.indexOf(w) === -1) { 1159 for (var j = 0; j < replaceSuffixes.length; j++) { 1160 if (w.endsWith(replaceSuffixes[j])) { 1161 w = w.slice(0, -replaceSuffixes[j].length) + 'i'; 1162 break; 1163 } 1164 } 1165 } 1166 return w; 1167 } 1168 1169 window.exampleSearch = function() { 1170 var term = window.getKeyWord(); 1171 if (!term) return; 1172 if (term.length < 2) { 1173 window.showMessage('请è¾å ¥è³å°ä¸¤ä¸ªå符ï¼'); 1174 return; 1175 } 1176 window.callApi('exampleSearch/' + encodeURIComponent(term)).then(function(r) { 1177 if (r === '2') window.setWebBrowser(term + ' è¯åºä¸ææ '); 1178 else if (r === '3') window.showMessage('æå¡æ¥é'); 1179 }); 1180 }; 1181 1182 window.callApi = function(endpoint) { 1183 return fetch('http://' + window.location.host + '/' + endpoint) 1184 .then(function(r) { return r.json(); }) 1185 .then(function(d) { window.displayResults(d); return d.length ? '1' : '2'; }) 1186 .catch(function() { return '3'; }); 1187 }; 1188 1189 window.displayResults = function(data) { 1190 var lb = document.getElementById('listBox'); 1191 var wb = document.getElementById('webBrowser'); 1192 if (!lb || !wb) return; 1193 lb.innerHTML = ''; 1194 wb.innerHTML = ''; 1195 data.forEach(function(item) { 1196 var btn = document.createElement('button'); 1197 btn.classList.add('list-item'); 1198 btn.textContent = item.word; 1199 btn.addEventListener('click', function() { window.displayDefinition(item); }); 1200 lb.appendChild(btn); 1201 }); 1202 if (data.length > 0) window.displayDefinition(data[0]); 1203 }; 1204 1205 // ==================== ç份åå¸å¼¹çª ==================== 1206 1207function showProvincePopup() { 1208 var popup = document.getElementById('provincePopup'); 1209 if (!popup) { 1210 popup = document.createElement('div'); 1211 popup.id = 'provincePopup'; 1212 popup.style.cssText = [ 1213 'display:none', 1214 'position:fixed', 1215 'z-index:99999', 1216 'top:50%', 1217 'left:50%', 1218 'transform:translate(-50%,-50%)', 1219 'background:#fff', 1220 'border:2px solid #006400', 1221 'border-radius:8px', 1222 'box-shadow:0 4px 20px rgba(0,0,0,0.3)', 1223 'width:360px', 1224 'max-height:500px', 1225 'overflow:auto', 1226 'font-family:monospace' 1227 ].join(';'); 1228 1229 popup.innerHTML = [ 1230 '<div style="background:#006400;color:#fff;padding:10px 15px;font-weight:bold;font-size:16px;', 1231 'display:flex;justify-content:space-between;align-items:center;', 1232 'border-radius:6px 6px 0 0;position:sticky;top:0;z-index:1;">', 1233 '<span>访é®å°åºåå¸</span>', 1234 '<span onclick="closeProvincePopup()" style="cursor:pointer;padding:2px 6px;border-radius:4px;">✖</span>', 1235 '</div>', 1236 '<div id="provincePopupContent" style="padding:15px;"></div>' 1237 ].join(''); 1238 1239 document.body.appendChild(popup); 1240 1241 // ç¹å»å¤é¨å ³é 1242 document.addEventListener('mousedown', function(e) { 1243 if (popup.style.display === 'block' && !popup.contains(e.target)) { 1244 closeProvincePopup(); 1245 } 1246 }); 1247 // ESCå ³é 1248 document.addEventListener('keydown', function(e) { 1249 if (e.key === 'Escape') closeProvincePopup(); 1250 }); 1251 } 1252 1253 document.getElementById('provincePopupContent').innerHTML = 1254 '<div style="text-align:center;color:#999;padding:20px;">å è½½ä¸...</div>'; 1255 popup.style.display = 'block'; 1256 1257 fetch('http://' + window.location.host + '/provinceStats?_=' + Date.now()) 1258 .then(function(r) { return r.json(); }) 1259 .then(function(d) { 1260 var html = '<table style="width:100%;border-collapse:collapse;font-size:14px;">'; 1261 html += '<tr style="background:#f0f0f0;">' + 1262 '<th style="text-align:left;padding:8px;border-bottom:1px solid #ddd;">å°åº</th>' + 1263 '<th style="text-align:right;padding:8px;border-bottom:1px solid #ddd;">人æ°</th></tr>'; 1264 1265 var total = 0; 1266 for (var province in d.provinces) { 1267 var count = d.provinces[province]; 1268 total += count; 1269 html += '<tr>' + 1270 '<td style="padding:8px;border-bottom:1px solid #eee;">' + escapeHtml(province) + '</td>' + 1271 '<td style="text-align:right;padding:8px;border-bottom:1px solid #eee;">' + count + '</td></tr>'; 1272 } 1273 1274 html += '<tr style="font-weight:bold;color:#006400;background:#f9f9f9;">' + 1275 '<td style="padding:8px;">å计</td>' + 1276 '<td style="text-align:right;padding:8px;">' + total + '</td></tr>'; 1277 html += '</table>'; 1278 1279 document.getElementById('provincePopupContent').innerHTML = html; 1280 }) 1281 .catch(function() { 1282 document.getElementById('provincePopupContent').innerHTML = 1283 '<div style="color:#c00;text-align:center;padding:20px;">å 载失败</div>'; 1284 }); 1285} 1286 1287window.closeProvincePopup = function() { 1288 var p = document.getElementById('provincePopup'); 1289 if (p) p.style.display = 'none'; 1290}; 1291
1292// ç®åçHTML转ä¹ï¼å¼¹çªå 使ç¨ï¼ 1293function escapeHtml(text) { 1294 if (!text) return ''; 1295 var div = document.createElement('div'); 1296 div.textContent = text; 1297 return div.innerHTML; 1298} 1299 1300 1301 // ==================== å¨è¯æ¡å®ä¹ä¸æ·»å PIV2020 龿¥ ==================== 1302 1303 function addPivLinksToDefinition() { 1304 var w = document.getElementById('webBrowser'); 1305 if (!w) return; 1306 1307 // å¤ç kapvorto èç¹ï¼å¨é³é¢é¾æ¥ææ é³é¢æ¶ç´æ¥å¨ kapvorto åæ·»å 1308 var kapvortos = w.querySelectorAll('strong.kapvorto'); 1309 kapvortos.forEach(function(kw) { 1310 // æ¸ çè¯æ¡åï¼å»æå导*ã|ã䏿 æ°åçå¹²æ°å符 1311 var wordText = kw.textContent.replace(/^\*+/, '').replace(/\|/g, '').replace(/[¹²³â´âµâ¶â·â¸â¹â°]/g, '').trim(); 1312 if (!wordText) return; 1313 1314 // æ£æ¥æ¯å¦å·²æ·»å è¿ PIV 龿¥ 1315 var parent = kw.parentNode; 1316 if (kw.nextElementSibling && kw.nextElementSibling.classList && kw.nextElementSibling.classList.contains('piv-link')) 1317 return; 1318 1319 var pivLink = document.createElement('span'); 1320 pivLink.className = 'piv-link'; 1321 pivLink.style.cssText = 'cursor:pointer;color:#006400;font-weight:bold;font-size:12px;margin-left:6px;vertical-align:middle;'; 1322 pivLink.textContent = '[PIV2020]'; 1323 pivLink.onclick = function(e) { 1324 e.stopPropagation(); 1325 showPiv2020Popup(wordText); 1326 }; 1327 1328 // 妿æé³é¢é¾æ¥ï¼æå¨é³é¢é¾æ¥åé¢ï¼å¦åæå¨ kapvorto åé¢ 1329 var nextEl = kw.nextElementSibling; 1330 if (nextEl && nextEl.tagName === 'A' && nextEl.getAttribute('href') && nextEl.getAttribute('href').indexOf('.mp3') > 0) { 1331 nextEl.parentNode.insertBefore(pivLink, nextEl.nextSibling); 1332 } else { 1333 kw.parentNode.insertBefore(pivLink, kw.nextSibling); 1334 } 1335 }); 1336 1337 // å¤ç pais èç¹ï¼å¨ </strong> å颿·»å 1338 var paisNodes = w.querySelectorAll('strong.pais'); 1339 paisNodes.forEach(function(pn) { 1340 // æ¸ çè¯æ¡åï¼å»æå导*ã|ã䏿 æ°åçå¹²æ°å符 1341 var wordText = pn.textContent.replace(/^\*+/, '').replace(/\|/g, '').replace(/[¹²³â´âµâ¶â·â¸â¹â°]/g, '').trim(); 1342 if (!wordText) return; 1343 1344 // æ£æ¥æ¯å¦å·²æ·»å è¿ 1345 if (pn.nextElementSibling && pn.nextElementSibling.classList && pn.nextElementSibling.classList.contains('piv-link')) 1346 return; 1347 1348 var pivLink = document.createElement('span'); 1349 pivLink.className = 'piv-link'; 1350 pivLink.style.cssText = 'cursor:pointer;color:#006400;font-weight:bold;font-size:11px;margin-left:5px;vertical-align:middle;'; 1351 pivLink.textContent = '[PIV2020]'; 1352 pivLink.onclick = function(e) { 1353 e.stopPropagation(); 1354 showPiv2020Popup(wordText); 1355 }; 1356 1357 pn.parentNode.insertBefore(pivLink, pn.nextSibling); 1358 }); 1359 } 1360 1361 // ==================== PIV2020 å¼¹çªï¼æ¹ä¸º iframe ç´æ¥å è½½ï¼é¿å API å¹é åå·®ï¼==================== 1362 1363 window.showPiv2020Popup = function(term) { 1364 var popup = document.getElementById('piv2020Popup'); 1365 var title = document.getElementById('piv2020Title'); 1366 var content = document.getElementById('piv2020Content'); 1367 if (!popup || !title || !content) return; 1368 1369 var cleanTerm = term.replace(/^\*+/, '').replace(/\|/g, '').replace(/[¹²³â´âµâ¶â·â¸â¹â°]/g, '').trim(); 1370 title.textContent = 'PIV2020: ' + cleanTerm; 1371 1372 // ç§»å¨ç«¯èªå¨è°æ´å°ºå¯¸åä½ç½® 1373 if (window.innerWidth < 768) { 1374 popup.style.width = '94vw'; 1375 popup.style.height = '70vh'; 1376 popup.style.left = '3vw'; 1377 popup.style.top = '10vh'; 1378 popup.style.resize = 'none'; 1379 } else { 1380 // æ¡é¢ç«¯ï¼å¦æä¹åè¢«ææºå°ºå¯¸è¦çè¿ï¼æ¢å¤é»è®¤å¼ 1381 if (popup.style.width === '94vw') { 1382 popup.style.width = '700px'; 1383 popup.style.height = '500px'; 1384 popup.style.left = '100px'; 1385 popup.style.top = '100px'; 1386 popup.style.resize = 'both'; 1387 } 1388 } 1389 1390 var pivUrl = 'https://vortaro.net/#' + encodeURIComponent(cleanTerm) + '_kd'; 1391 content.innerHTML = '<iframe src="' + pivUrl + '" style="width:100%;height:100%;border:none;" title="PIV2020"></iframe>'; 1392 1393 popup.style.display = 'block'; 1394 }; 1395 1396 window.closePiv2020Popup = function() { 1397 var popup = document.getElementById('piv2020Popup'); 1398 if (popup) popup.style.display = 'none'; 1399 }; 1400 1401 document.addEventListener('mousedown', function(e) { 1402 var popup = document.getElementById('piv2020Popup'); 1403 if (popup && popup.style.display === 'block' && !popup.contains(e.target)) { 1404 closePiv2020Popup(); 1405 } 1406 });
1407 document.addEventListener('keydown', function(e) { 1408 if (e.key === 'Escape') closePiv2020Popup(); 1409 }); 1410 1411 // ==================== çè¨æ¿åè½ ==================== 1412 1413/** 1414 * æ¾ç¤ºçè¨æ¿å¼¹çª 1415 */ 1416window.showGuestbookPopup = function() { 1417 var popup = document.getElementById('guestbookPopup'); 1418 if (!popup) return; 1419 1420 // ææºç«¯éç½®ä½ç½®å°é¡¶é¨ 1421 if (window.innerWidth < 768) { 1422 popup.style.transform = 'none'; 1423 popup.style.top = '4vh'; 1424 popup.style.left = '3vw'; 1425 } else { 1426 // æ¡é¢ç«¯å± ä¸ 1427 popup.style.transform = 'translate(-50%, -50%)'; 1428 popup.style.top = '50%'; 1429 popup.style.left = '50%'; 1430 } 1431 1432 popup.style.display = 'block'; 1433 loadGuestbookList(); 1434 // æ¸ ç©ºè¡¨å 1435 document.getElementById('gbSubject').value = ''; 1436 document.getElementById('gbContent').value = ''; 1437 document.getElementById('gbImage').value = ''; 1438 document.getElementById('gbMessage').textContent = ''; 1439}; 1440 1441 /** 1442 * å ³éçè¨æ¿å¼¹çª 1443 */ 1444 window.closeGuestbookPopup = function() { 1445 var popup = document.getElementById('guestbookPopup'); 1446 if (popup) popup.style.display = 'none'; 1447 }; 1448 1449/** 1450 * æäº¤çè¨ 1451 */ 1452window.submitGuestbook = function(event) { 1453 event.preventDefault(); 1454 var subjectEl = document.getElementById('gbSubject'); 1455 var contentEl = document.getElementById('gbContent'); 1456 var imageEl = document.getElementById('gbImage'); 1457 var msgEl = document.getElementById('gbMessage'); 1458 1459 var subject = subjectEl ? subjectEl.value.trim() : ''; 1460 var content = contentEl ? contentEl.value.trim() : ''; 1461 var imageFile = imageEl ? imageEl.files[0] : null; 1462 1463 if (!subject || !content) { 1464 if (msgEl) { 1465 msgEl.style.color = '#c00'; 1466 msgEl.textContent = '请填å主é¢å详ç»å 容ï¼'; 1467 } 1468 return; 1469 } 1470 1471 var formData = new FormData(); 1472 formData.append('subject', subject); 1473 formData.append('content', content); 1474 if (imageFile) { 1475 if (imageFile.size > 5 * 1024 * 1024) { 1476 if (msgEl) { 1477 msgEl.style.color = '#c00'; 1478 msgEl.textContent = 'å¾çä¸è½è¶ è¿ 5MBï¼'; 1479 } 1480 return; 1481 } 1482 formData.append('image', imageFile); 1483 } 1484 1485 if (msgEl) { 1486 msgEl.style.color = '#006400'; 1487 msgEl.textContent = 'æäº¤ä¸...'; 1488 } 1489 1490 // æå° FormData å 容ï¼è°è¯ï¼ 1491 console.log('=== æäº¤çè¨ ==='); 1492 console.log('主é¢:', subject); 1493 console.log('å 容:', content); 1494 console.log('å¾ç:', imageFile ? imageFile.name + ' (' + imageFile.size + ' bytes)' : 'æ '); 1495 console.log('å¾çç±»å:', imageFile ? imageFile.type : 'æ '); 1496 1497 fetch('/api/guestbook/submit', { 1498 method: 'POST', 1499 body: formData 1500 }) 1501 .then(function(response) { 1502 console.log('ååºç¶æ:', response.status, response.statusText); 1503 // å 读åååºææ¬ 1504 return response.text().then(function(text) { 1505 console.log('ååºå 容:', text); 1506 try { 1507 return { status: response.status, data: JSON.parse(text) }; 1508 } catch(e) { 1509 console.error('JSONè§£æå¤±è´¥:', e); 1510 return { status: response.status, data: { error: text || 'æå¡å¨è¿åéJSONæ ¼å¼' } }; 1511 } 1512 }); 1513 }) 1514 .then(function(result) { 1515 console.log('è§£æç»æ:', result); 1516 if (msgEl) { 1517 if (result.data && result.data.success) { 1518 msgEl.style.color = '#006400'; 1519 msgEl.textContent = 'â çè¨æäº¤æåï¼'; 1520 if (subjectEl) subjectEl.value = ''; 1521 if (contentEl) contentEl.value = ''; 1522 if (imageEl) imageEl.value = ''; 1523 loadGuestbookList(); 1524 } else { 1525 var errorMsg = (result.data && result.data.error) ? result.data.error : 'æäº¤å¤±è´¥ï¼è¯·éè¯'; 1526 msgEl.style.color = '#c00'; 1527 msgEl.textContent = 'â ' + errorMsg; 1528 console.error('æå¡å¨è¿åé误:', result); 1529 } 1530 } 1531 }) 1532 .catch(function(err) { 1533 console.error('ç½ç»è¯·æ±å¤±è´¥:', err); 1534 if (msgEl) { 1535 msgEl.style.color = '#c00'; 1536 msgEl.textContent = 'â ç½ç»é误: ' + (err.message || '请éè¯'); 1537 } 1538 });
1539}; 1540 1541 /** 1542 * å è½½çè¨å表 1543 */ 1544 window.loadGuestbookList = function() { 1545 var listEl = document.getElementById('guestbookList'); 1546 if (!listEl) return; 1547 listEl.innerHTML = '<div style="text-align:center;color:#999;padding:10px;">å è½½ä¸...</div>'; 1548 1549 fetch('http://' + window.location.host + '/api/guestbook/list') 1550 .then(function(r) { return r.json(); }) 1551 .then(function(data) { 1552 if (!data.success || !data.messages || data.messages.length === 0) { 1553 listEl.innerHTML = '<div style="text-align:center;color:#999;padding:15px;">ð ææ çè¨ï¼å¿«æ¥åä¸ç¬¬ä¸æ¡å§ï¼</div>'; 1554 return; 1555 } 1556 var html = ''; 1557 data.messages.forEach(function(msg) { 1558 var time = new Date(msg.time * 1000).toLocaleString('zh-CN'); 1559 var imageHtml = msg.image ? '<div style="margin-top:8px;"><img src="' + msg.image + '" style="max-width:100%;max-height:200px;border-radius:4px;border:1px solid #eee;"></div>' : ''; 1560 html += '<div style="border-bottom:1px solid #f0f0f0;padding:12px 0;">'; 1561 html += '<div style="font-weight:bold;font-size:15px;color:#006400;">' + escapeHtml(msg.subject) + '</div>'; 1562 html += '<div style="font-size:13px;color:#666;margin:4px 0;">' + escapeHtml(msg.content) + '</div>'; 1563 html += imageHtml; 1564 html += '<div style="font-size:11px;color:#999;margin-top:6px;">' + time + ' · IP: ' + escapeHtml(msg.ip) + '</div>'; 1565 html += '</div>'; 1566 }); 1567 listEl.innerHTML = html; 1568 }) 1569 .catch(function() { 1570 listEl.innerHTML = '<div style="text-align:center;color:#c00;padding:10px;">å è½½çè¨å¤±è´¥</div>'; 1571 }); 1572 }; 1573 1574 /** 1575 * æ¾ç¤ºç®¡çååå° 1576 */ 1577 window.showAdminPopup = function() { 1578 var popup = document.getElementById('adminPopup'); 1579 if (!popup) return; 1580 // ææºç«¯éç½®ä½ç½®å°é¡¶é¨ 1581 if (window.innerWidth < 768) { 1582 popup.style.transform = 'none'; 1583 popup.style.top = '4vh'; 1584 popup.style.left = '3vw'; 1585 } else { 1586 // æ¡é¢ç«¯å± ä¸ 1587 popup.style.transform = 'translate(-50%, -50%)'; 1588 popup.style.top = '50%'; 1589 popup.style.left = '50%'; 1590 } 1591 popup.style.display = 'block'; 1592 var listEl = document.getElementById('adminList'); 1593 if (listEl) listEl.innerHTML = '<div style="text-align:center;color:#999;padding:20px;font-size:14px;">请è¾å ¥å¯ç ç»å½</div>'; 1594 var pwdEl = document.getElementById('adminPassword'); 1595 if (pwdEl) pwdEl.value = ''; 1596 }; 1597 1598 window.closeAdminPopup = function() { 1599 var popup = document.getElementById('adminPopup'); 1600 if (popup) popup.style.display = 'none'; 1601 }; 1602 1603 1604 /** 1605 * å 载管çå颿¿ 1606 */ 1607 window.loadAdminPanel = function() { 1608 var pwdEl = document.getElementById('adminPassword'); 1609 var listEl = document.getElementById('adminList'); 1610 var password = pwdEl ? pwdEl.value.trim() : ''; 1611 1612 if (!password) { 1613 if (listEl) listEl.innerHTML = '<div style="color:#c00;text-align:center;padding:10px;">请è¾å ¥å¯ç </div>'; 1614 return; 1615 } 1616 1617 if (listEl) listEl.innerHTML = '<div style="text-align:center;color:#999;padding:20px;">å è½½ä¸...</div>'; 1618 1619 fetch('http://' + window.location.host + '/api/guestbook/admin?password=' + encodeURIComponent(password)) 1620 .then(function(r) { return r.json(); }) 1621 .then(function(data) { 1622 if (!listEl) return; 1623 if (!data.success) { 1624 listEl.innerHTML = '<div style="color:#c00;text-align:center;padding:10px;">â ' + (data.error || 'å¯ç é误') + '</div>'; 1625 return; 1626 } 1627 var msgs = data.messages || []; 1628 if (msgs.length === 0) { 1629 listEl.innerHTML = '<div style="text-align:center;color:#999;padding:20px;">ææ çè¨</div>'; 1630 return; 1631 } 1632 var html = '<div style="font-size:13px;color:#666;margin-bottom:10px;">å ± ' + msgs.length + ' æ¡çè¨</div>';
1633 msgs.forEach(function(msg, idx) { 1634 var time = new Date(msg.time * 1000).toLocaleString('zh-CN'); 1635 var imageHtml = msg.image ? '<a href="' + msg.image + '" target="_blank" style="color:#006400;">æ¥çå¾ç</a>' : 'æ '; 1636 html += '<div style="border-bottom:1px solid #eee;padding:10px 0;font-size:14px;">'; 1637 html += '<div style="display:flex;justify-content:space-between;flex-wrap:wrap;">'; 1638 html += '<span style="font-weight:bold;">#' + (idx + 1) + ' ' + escapeHtml(msg.subject) + '</span>'; 1639 html += '<span style="color:#999;font-size:12px;">' + time + '</span>'; 1640 html += '</div>'; 1641 html += '<div style="margin:4px 0;color:#333;">' + escapeHtml(msg.content) + '</div>'; 1642 html += '<div style="font-size:12px;color:#999;">IP: ' + escapeHtml(msg.ip) + ' | å¾ç: ' + imageHtml; 1643 html += ' <button onclick="window.deleteGuestbook(' + msg.id + ')" style="color:#c00;border:none;background:none;cursor:pointer;font-size:12px;">å é¤</button>'; 1644 html += '</div>'; 1645 html += '</div>'; 1646 }); 1647 listEl.innerHTML = html; 1648 }) 1649 .catch(function() { 1650 if (listEl) listEl.innerHTML = '<div style="color:#c00;text-align:center;padding:10px;">å 载失败</div>'; 1651 }); 1652 }; 1653 1654 /** 1655 * å é¤çè¨ï¼ç®¡çåï¼ 1656 */ 1657 window.deleteGuestbook = function(id) { 1658 if (!confirm('ç¡®å®è¦å é¤è¿æ¡çè¨åï¼')) return; 1659 var pwdEl = document.getElementById('adminPassword'); 1660 var password = pwdEl ? pwdEl.value.trim() : ''; 1661 1662 fetch('http://' + window.location.host + '/api/guestbook/delete', { 1663 method: 'POST', 1664 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 1665 body: 'id=' + id + '&password=' + encodeURIComponent(password) 1666 }) 1667 .then(function(r) { return r.json(); }) 1668 .then(function(data) { 1669 if (data.success) { 1670 loadAdminPanel(); 1671 loadGuestbookList(); 1672 } else { 1673 alert('å é¤å¤±è´¥: ' + (data.error || 'æªç¥é误')); 1674 } 1675 }) 1676 .catch(function() { 1677 alert('ç½ç»é误'); 1678 }); 1679 }; 1680 1681 // ç¹å»å¼¹çªå¤å ³é 1682 document.addEventListener('mousedown', function(e) { 1683 var popup = document.getElementById('guestbookPopup'); 1684 if (popup && popup.style.display === 'block' && !popup.contains(e.target)) { 1685 // ä¸èªå¨å ³éï¼è®©ç¨æ·ç¹å» à æåæ¶æé® 1686 } 1687 var adminPopup = document.getElementById('adminPopup'); 1688 if (adminPopup && adminPopup.style.display === 'block' && !adminPopup.contains(e.target)) { 1689 // ä¸èªå¨å ³é 1690 } 1691 }); 1692 1693})(); 1694
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.