{"repo": "adachissss/ecological-environment-data-space", "files": {"public/receiver/index.js": {"ee121648c0a86c43d21f273b21df7b9492756ddc": {"t": 1785768939.3012965, "n": 31833, "h": "7c3901c66d38b85e", "c": "// 配置\n// 优先使用 config.js 的配置,否则使用当前域名\nconst API_BASE_URL = (typeof API_BASE !== 'undefined' ? API_BASE : window.location.origin);\nlet currentTab = \"animal\";\nlet currentPage = 1;\nconst pageLimit = 10;\nlet totalPages = 1;\n\n// 页面加载时初始化\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n checkAuth();\n loadUserInfo();\n loadAllCounts(); // 加载所有资产统计\n loadAssets(currentTab);\n});\n\n// 检查登录状态\nfunction checkAuth() {\n const token = localStorage.getItem(\"token\");\n if (!token) {\n alert(\"请先登录\");\n window.location.href = \"../login.html\";\n return;\n }\n}\n\n// 获取 token\nfunction getToken() {\n return localStorage.getItem(\"token\");\n}\n\n// 加载用户信息\nasync function loadUserInfo() {\n try {\n const userId = localStorage.getItem(\"userId\");\n if (userId) {\n document.getElementById(\"username\").textContent = userId;\n }\n } catch (error) {\n console.error(\"加载用户信息失败:\", error);\n }\n}\n\n// 退出登录\nfunction logout() {\n localStorage.removeItem(\"token\");\n localStorage.removeItem(\"userId\");\n localStorage.removeItem(\"institution\");\n window.location.href = \"../login.html\";\n}\n\n// 加载所有资产类型的统计数字\nasync function loadAllCounts() {\n const types = ['animal', 'lake', 'air', 'plant'];\n \n for (const type of types) {\n try {\n const response = await fetch(`${API_BASE_URL}/org2/assets?type=${type}&page=1&limit=1`, {\n headers: {\n Authorization: `Bearer ${getToken()}`,\n },\n });\n\n if (response.ok) {\n const result = await response.json();\n if (result.success && result.data) {\n document.getElementById(`${type}-count`).textContent = result.data.total;\n }\n }\n } catch (error) {\n console.error(`加载${type}统计失败:`, error);\n document.getElementById(`${type}-count`).textContent = '0';\n }\n }\n}\n\n// 切换标签\nfunction switchTab(tab) {\n currentTab = tab;\n currentPage = 1;\n\n // 更新按钮状态\n const buttons = document.querySelectorAll(\".tab-btn\");\n buttons.forEach((btn) => {\n btn.classList.remove(\"active\");\n // 简单的匹配逻辑\n if (btn.textContent.toLowerCase().includes(tab.replace('-verify',''))) {\n btn.classList.add(\"active\");\n } else if (tab === 'permissions' && btn.textContent.includes('申请')) {\n btn.classList.add(\"active\");\n } else if (tab === 'hash-verify' && btn.textContent.includes('验证')) {\n btn.classList.add(\"active\");\n }\n });\n\n // 加载对应数据\n if (tab === 'permissions') {\n loadMyPermissions();\n } else if (tab === 'hash-verify') {\n loadHashVerifyTool();\n } else {\n loadAssets(tab);\n }\n}\n\n// 加载资产数据\nasync function loadAssets(type) {\n const tableContent = document.getElementById(\"table-content\");\n tableContent.innerHTML = `\n
\n
\n

正在加载数据...

\n
\n `;\n\n try {\n const response = await fetch(`${API_BASE_URL}/org2/assets?type=${type}&page=${currentPage}&limit=${pageLimit}`, {\n headers: {\n Authorization: `Bearer ${getToken()}`,\n },\n });\n\n if (response.status === 401) {\n alert(\"登录已过期,请重新登录\");\n logout();\n return;\n }\n\n const result = await response.json();\n\n if (result.success && result.data) {\n const { assets, total } = result.data;\n totalPages = Math.ceil(total / pageLimit);\n \n // 更新统计数字\n document.getElementById(`${type}-count`).textContent = total;\n \n // 渲染表格\n renderTable(assets, type);\n \n // 显示分页\n updatePagination();\n } else {\n throw new Error(result.message || '加载失败');\n }\n } catch (error) {\n console.error(\"加载资产数据失败:\", error);\n tableContent.innerHTML = `\n
\n

❌ 加载失败: ${error.message}

\n \n
\n `;\n }\n}\n\n// 渲染表格\nfunction renderTable(assets, type) {\n const tableContent = document.getElementById(\"table-content\");\n\n if (assets.length === 0) {\n tableContent.innerHTML = `\n
\n

暂无数据 / No Data Available

\n
\n `;\n return;\n }\n\n let tableHTML = `\n \n \n \n \n \n \n ${type === 'animal' || type === 'plant' ? '' : ''}\n \n \n \n \n \n `;\n\n assets.forEach(asset => {\n const permissionBadge = getPermissionBadge(asset.permissionStatus);\n tableHTML += `\n \n \n \n \n ${type === 'animal' || type === 'plant' ? `` : ''}\n \n \n \n `;\n });\n\n tableHTML += `\n \n
IDBusiness IDNameSpeciesPermissionAction
${asset.globalId}${asset.businessId}${asset.name}${asset.species || '-'}${permissionBadge}\n \n
\n `;\n\n tableContent.innerHTML = tableHTML;\n}\n\n// 获取权限状态徽章\nfunction getPermissionBadge(status) {\n if (status === null || status === undefined) {\n return '未申请 None';\n } else if (status === 0) {\n return '审核中 Pending';\n } else if (status === 1) {\n return '已通过 Approved';\n } else if (status === 2) {\n return '已驳回 Rejected';\n }\n return 'Unknown';\n}\n\n// 查看资产详情\nasync function viewAssetDetail(type, globalId) {\n const modal = document.getElementById(\"detailModal\");\n modal.style.display = \"flex\";\n modal.classList.add(\"show\"); // 添加 show 类触发动画\n\n // 显示加载状态\n document.getElementById(\"detail-basic-info\").innerHTML = `\n

加载中...

\n `;\n document.getElementById(\"permission-status-section\").innerHTML = `\n

加载中...

\n `;\n document.getElementById(\"chain-data-section\").style.display = \"none\";\n\n try {\n const response = await fetch(`${API_BASE_URL}/org2/assets/${type}/${globalId}`, {\n headers: {\n Authorization: `Bearer ${getToken()}`,\n },\n });\n\n const result = await response.json();\n\n if (result.success && result.data) {\n const asset = result.data;\n \n // 显示基本信息\n let basicInfoHTML = `\n
\n GLOBAL ID\n ${asset.globalId}\n
\n
\n BUSINESS ID\n ${asset.businessId}\n
\n
\n NAME\n ${asset.name}\n
\n `;\n \n if (asset.species) {\n basicInfoHTML += `\n
\n SPECIES\n ${asset.species}\n
\n `;\n }\n \n document.getElementById(\"detail-basic-info\").innerHTML = basicInfoHTML;\n\n // 显示权限状态\n renderPermissionStatus(asset, type);\n\n } else {\n throw new Error(result.message || '加载失败');\n }\n } catch (error) {\n console.error(\"加载资产详情失败:\", error);\n document.getElementById(\"detail-basic-info\").innerHTML = `\n

加载失败: ${error.message}

\n `;\n }\n}\n\n// 渲染权限状态\nfunction renderPermissionStatus(asset, type) {\n const section = document.getElementById(\"permission-status-section\");\n \n if (asset.permissionStatus === null || asset.permissionStatus === undefined) {\n // 未申请\n section.innerHTML = `\n

您还未申请访问该资产的权限 / No access permission

\n \n `;\n } else if (asset.permissionStatus === 0) {\n // 审核中\n section.innerHTML = `\n

⏳ 权限申请审核中 / Audit Pending...

\n `;\n } else if (asset.permissionStatus === 1) {\n // 已通过\n section.innerHTML = `\n

✅ 权限审核已通过 / Access Granted

\n \n `;\n } else if (asset.permissionStatus === 2) {\n // 已驳回\n section.innerHTML = `\n

❌ 权限申请已被驳回 / Request Rejected

\n `;\n }\n}\n\n// 申请权限\nasync function applyPermission(globalId, type) {\n if (!confirm('Confirm to apply for access?')) {\n return;\n }\n\n try {\n const response = await fetch(`${API_BASE_URL}/org2/permission/apply`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${getToken()}`,\n },\n body: JSON.stringify({ globalId })\n });\n\n const result = await response.json();\n\n if (result.success) {\n alert('✅ ' + result.message);\n // 重新加载详情\n viewAssetDetail(type, globalId);\n } else {\n alert('❌ ' + result.message);\n }\n } catch (error) {\n console.error(\"申请权限失败:\", error);\n alert('申请失败: ' + error.message);\n }\n}\n\n// ==================== AI 功能模块 ====================\n// 保持逻辑不变,仅修改生成的HTML样式以适应黑白灰主题\n\nlet currentChainData = []; \nlet currentAssetType = '';\nlet currentBusinessId = '';\nlet conversationHistory = []; \n\n// 修改原有的 loadChainData 函数\nwindow.loadChainData = async function(type, businessId, assetName) {\n const section = document.getElementById(\"chain-data-section\");\n const content = document.getElementById(\"chain-data-content\");\n \n section.style.display = \"block\";\n content.innerHTML = `\n
\n
\n

Loading Blockchain Data...

\n
\n `;\n\n try {\n const response = await fetch(`${API_BASE_URL}/org2/chain-data/${type}/${businessId}`, {\n headers: {\n Authorization: `Bearer ${getToken()}`,\n },\n });\n\n const result = await response.json();\n\n if (result.success) {\n const chainData = result.data;\n currentChainData = chainData;\n currentAssetType = type;\n currentBusinessId = businessId;\n \n if (!chainData || chainData.length === 0) {\n content.innerHTML = `

暂无链上数据 / No Data on Chain

`;\n return;\n }\n\n // 渲染链上数据\n let dataHTML = `
`;\n dataHTML += `

Blockchain Records (${chainData.length})

`;\n \n chainData.forEach((item, index) => {\n const timestamp = item.Timestamp || item.timestamp || item.timestampMs ? new Date(item.timestampMs || item.timestamp).toLocaleString('zh-CN') : 'Unknown Time';\n const recordId = `record-${businessId}-${index}`;\n window[recordId] = item;\n \n dataHTML += `\n
\n
\n
Record #${index + 1} - ${timestamp}
\n \n
\n
${JSON.stringify(item, null, 2)}
\n
\n `;\n });\n dataHTML += `
`;\n \n content.innerHTML = dataHTML;\n showAIFeatures(); // 显示 AI 按钮\n } else {\n content.innerHTML = `

${result.message}

`;\n }\n } catch (error) {\n console.error(\"加载链上数据失败:\", error);\n content.innerHTML = `

加载失败: ${error.message}

`;\n }\n};\n\n// 显示 AI 功能按钮(样式已修改为适应新主题)\nfunction showAIFeatures() {\n const section = document.getElementById(\"chain-data-section\");\n const existingAI = document.getElementById('ai-features-container');\n if (existingAI) existingAI.remove();\n \n const aiContainer = document.createElement('div');\n aiContainer.id = 'ai-features-container';\n // 内联样式被移除,转而在 CSS 中定义 #ai-features-container\n aiContainer.innerHTML = `\n
\n \n \n \n Analyzing...\n \n
\n \n
\n

\n AI Analysis Report\n

\n
\n
\n \n
\n
\n \n
\n
\n
\n

\n 💬 AI Assistant\n

\n \n
\n
\n
\n
🤖
\n

Feel free to ask questions about the data.

\n
\n
\n
\n \n \n
\n
\n
\n `;\n \n section.appendChild(aiContainer);\n \n // 绑定事件\n document.getElementById('ai-analysis-btn').onclick = performAIAnalysis;\n document.getElementById('ai-chat-btn').onclick = openAIChat;\n document.getElementById('ai-chat-send').onclick = sendAIQuestion;\n document.getElementById('ai-chat-close-top').onclick = closeAIChat;\n document.getElementById('ai-chat-input').onkeypress = (e) => {\n if (e.key === 'Enter') sendAIQuestion();\n };\n}\n\n// 执行 AI 分析\nasync function performAIAnalysis() {\n const resultDiv = document.getElementById('ai-analysis-result');\n const contentDiv = document.getElementById('ai-analysis-content');\n const loadingSpan = document.getElementById('ai-loading');\n const tokensInfo = document.getElementById('ai-tokens-info');\n \n try {\n loadingSpan.style.display = 'inline-block';\n resultDiv.style.display = 'none';\n \n const response = await fetch(`${API_BASE_URL}/ai/analyze-chain-data`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${getToken()}`\n },\n body: JSON.stringify({\n assetType: currentAssetType,\n businessId: currentBusinessId,\n chainData: currentChainData\n })\n });\n \n const result = await response.json();\n \n if (result.success) {\n contentDiv.textContent = result.analysis;\n tokensInfo.textContent = `Token Usage: ${result.tokensUsed}`;\n resultDiv.style.display = 'block';\n } else {\n alert('AI Analysis Failed: ' + result.message);\n }\n \n } catch (error) {\n console.error('AI Error:', error);\n alert('Network error, please try again later.');\n } finally {\n loadingSpan.style.display = 'none';\n }\n}\n\n// 打开 AI 聊天\nfunction openAIChat() {\n const chatInterface = document.getElementById('ai-chat-interface');\n const chatMessages = document.getElementById('ai-chat-messages');\n \n conversationHistory = [];\n chatMessages.innerHTML = `\n
\n
🤖
\n

Feel free to ask questions about the data.

\n
\n `;\n \n chatInterface.style.display = 'block';\n document.getElementById('ai-chat-input').focus();\n chatInterface.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n}\n\nfunction closeAIChat() {\n document.getElementById('ai-chat-interface').style.display = 'none';\n}\n\n// 发送 AI 问题\nasync function sendAIQuestion() {\n const input = document.getElementById('ai-chat-input');\n const question = input.value.trim();\n \n if (!question) return;\n \n addChatMessage('user', question);\n input.value = '';\n \n const loadingId = addChatMessage('assistant', 'Thinking...');\n \n try {\n const assetNames = { 'animal': 'Animal', 'lake': 'Lake', 'plant': 'Plant', 'air': 'Air' };\n \n let context = `Asset Type: ${assetNames[currentAssetType] || currentAssetType}\\n`;\n context += `ID: ${currentBusinessId}\\n`;\n context += `Record Count: ${currentChainData.length}\\n`;\n context += `Data Summary: ${JSON.stringify(currentChainData.slice(0, 3))}`;\n \n const response = await fetch(`${API_BASE_URL}/ai/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${getToken()}`\n },\n body: JSON.stringify({\n question,\n context,\n conversationHistory\n })\n });\n \n const result = await response.json();\n document.getElementById(loadingId).remove();\n \n if (result.success) {\n addChatMessage('assistant', result.answer);\n conversationHistory.push(\n { role: 'user', content: question },\n { role: 'assistant', content: result.answer }\n );\n } else {\n addChatMessage('assistant', '❌ Error: ' + result.message);\n }\n } catch (error) {\n document.getElementById(loadingId).remove();\n addChatMessage('assistant', '❌ Connection failed');\n }\n}\n\n// 添加聊天消息(适配黑白配色)\nfunction addChatMessage(role, content) {\n const chatMessages = document.getElementById('ai-chat-messages');\n const welcomeDiv = chatMessages.querySelector('div[style*=\"text-align: center\"]');\n if (welcomeDiv) welcomeDiv.remove();\n \n const messageId = 'msg-' + Date.now();\n const messageDiv = document.createElement('div');\n messageDiv.id = messageId;\n \n // 只有这里为了简便使用了内联样式,但配色已改为黑白\n const userStyle = 'background: #1a1a1a; color: white; margin-left: auto; border-radius: 12px 12px 0 12px;';\n const aiStyle = 'background: white; color: #333; margin-right: auto; border: 1px solid #eee; border-radius: 12px 12px 12px 0;';\n \n messageDiv.style.cssText = `\n margin-bottom: 15px; padding: 12px 16px; max-width: 80%; font-size:13px;\n ${role === 'user' ? userStyle : aiStyle}\n animation: fadeIn 0.3s ease-in;\n `;\n \n messageDiv.innerHTML = `\n
\n ${role === 'user' ? 'ME' : 'AI'}\n
\n
${content}
\n `;\n \n chatMessages.appendChild(messageDiv);\n chatMessages.scrollTop = chatMessages.scrollHeight;\n return messageId;\n}\n\n// 导出单条记录 (保持原有逻辑)\nasync function exportSingleRecord(recordId, businessId, recordIndex, assetType, assetName) {\n try {\n const record = window[recordId];\n if (!record) throw new Error('Data not found');\n const token = localStorage.getItem(\"token\");\n if (!token) { alert(\"Please log in\"); return; }\n\n // 下载JSON\n const jsonResponse = await fetch('/org2/export/single-record', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },\n body: JSON.stringify({ assetType, businessId, recordIndex, recordData: record })\n });\n\n if (!jsonResponse.ok) throw new Error('Export failed');\n const jsonBlob = await jsonResponse.blob();\n const jsonUrl = window.URL.createObjectURL(jsonBlob);\n const jsonLink = document.createElement('a');\n jsonLink.href = jsonUrl;\n jsonLink.download = `${businessId}-record-${recordIndex}.json`;\n document.body.appendChild(jsonLink);\n jsonLink.click();\n document.body.removeChild(jsonLink);\n window.URL.revokeObjectURL(jsonUrl);\n\n await new Promise(r => setTimeout(r, 500));\n\n // 下载PDF\n const pdfResponse = await fetch('/org2/export/single-record?type=certificate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },\n body: JSON.stringify({ assetType, businessId, recordIndex, recordData: record })\n });\n \n if (!pdfResponse.ok) throw new Error('Certificate failed');\n const pdfBlob = await pdfResponse.blob();\n const pdfUrl = window.URL.createObjectURL(pdfBlob);\n const pdfLink = document.createElement('a');\n pdfLink.href = pdfUrl;\n pdfLink.download = `certificate-${businessId}-record-${recordIndex}.pdf`;\n document.body.appendChild(pdfLink);\n pdfLink.click();\n document.body.removeChild(pdfLink);\n window.URL.revokeObjectURL(pdfUrl);\n\n alert(`✅ Record #${recordIndex} Exported Successfully!`);\n } catch (error) {\n console.error('Export failed:', error);\n alert('❌ Export Failed: ' + error.message);\n }\n}\n\n// 加载我的权限申请列表\nasync function loadMyPermissions() {\n const tableContent = document.getElementById(\"table-content\");\n tableContent.innerHTML = `

Loading Permissions...

`;\n\n try {\n const response = await fetch(`${API_BASE_URL}/org2/permissions`, {\n headers: { Authorization: `Bearer ${getToken()}` },\n });\n const result = await response.json();\n if (result.success && result.data) {\n renderPermissionsTable(result.data);\n } else {\n throw new Error(result.message || 'Failed');\n }\n } catch (error) {\n tableContent.innerHTML = `

❌ Failed: ${error.message}

`;\n }\n}\n\n// 渲染权限申请表格\nfunction renderPermissionsTable(permissions) {\n const tableContent = document.getElementById(\"table-content\");\n if (permissions.length === 0) {\n tableContent.innerHTML = `

No Applications Found

`;\n return;\n }\n\n let tableHTML = `\n \n \n \n \n \n \n \n \n \n \n \n \n `;\n\n permissions.forEach(perm => {\n const permissionBadge = getPermissionBadge(perm.permissionStatus);\n tableHTML += `\n \n \n \n \n \n \n \n \n `;\n });\n\n tableHTML += `
App IDTypeBusiness IDNameStatusAction
${perm.id}${perm.assetType}${perm.businessId}${perm.name}${permissionBadge}\n \n
`;\n tableContent.innerHTML = tableHTML;\n document.getElementById(\"pagination\").style.display = \"none\";\n}\n\nfunction closeDetailModal() {\n const modal = document.getElementById(\"detailModal\");\n modal.classList.remove(\"show\"); // 移除 show 类触发淡出动画\n setTimeout(() => {\n modal.style.display = \"none\";\n }, 300);\n}\n\nfunction updatePagination() {\n const pagination = document.getElementById(\"pagination\");\n const pageInfo = document.getElementById(\"page-info\");\n const prevBtn = document.getElementById(\"prev-btn\");\n const nextBtn = document.getElementById(\"next-btn\");\n\n if (totalPages <= 1) {\n pagination.style.display = \"none\";\n return;\n }\n\n pagination.style.display = \"flex\";\n pageInfo.textContent = `Page ${currentPage} / ${totalPages}`;\n prevBtn.disabled = currentPage <= 1;\n nextBtn.disabled = currentPage >= totalPages;\n}\n\nfunction prevPage() {\n if (currentPage > 1) {\n currentPage--;\n loadAssets(currentTab);\n }\n}\n\nfunction nextPage() {\n if (currentPage < totalPages) {\n currentPage++;\n loadAssets(currentTab);\n }\n}\n\n// 哈希验证工具 (保持逻辑,美化HTML输出)\nfunction loadHashVerifyTool() {\n const tableContent = document.getElementById(\"table-content\");\n document.getElementById(\"pagination\").style.display = \"none\";\n\n tableContent.innerHTML = `\n
\n
\n

🔐 Data Integrity Verification

\n

Upload the JSON file to verify its SHA-256 hash against the certificate.

\n \n
\n
\n
📁
\n

Click or Drag JSON file here

\n \n
\n
\n\n
\n
\n

File Information

\n
Name:-
\n
Size:-
\n
Time:-
\n
\n\n
\n

SHA-256 Hash

\n
-
\n \n
\n\n
\n

Compare Hash

\n

Paste certificate hash below:

\n \n \n
\n
\n
\n
\n
\n `;\n\n const uploadArea = document.getElementById('upload-area');\n const fileInput = document.getElementById('file-input');\n\n uploadArea.onclick = () => fileInput.click();\n fileInput.onchange = (e) => { const file = e.target.files[0]; if (file) handleFileUpload(file); };\n\n uploadArea.ondragover = (e) => { e.preventDefault(); uploadArea.style.borderColor = '#000'; };\n uploadArea.ondragleave = (e) => { e.preventDefault(); uploadArea.style.borderColor = '#eee'; };\n uploadArea.ondrop = (e) => { e.preventDefault(); uploadArea.style.borderColor = '#eee'; const file = e.dataTransfer.files[0]; if (file) handleFileUpload(file); };\n}\n\n// 保持原有的 handleFileUpload, copyHash, compareHash, formatFileSize 函数逻辑\n// 仅微调 compareHash 的输出样式\nasync function handleFileUpload(file) {\n if (!file.name.endsWith('.json')) { alert('⚠️ Please upload JSON file'); return; }\n const startTime = Date.now();\n try {\n const fileContent = await file.text();\n let contentToHash = fileContent;\n try {\n const jsonData = JSON.parse(fileContent);\n if (jsonData.exportInfo && jsonData.exportInfo.dataHash) {\n contentToHash = JSON.stringify(jsonData.data, null, 2);\n }\n } catch (e) {}\n \n const encoder = new TextEncoder();\n const data = encoder.encode(contentToHash);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n \n const calcTime = Date.now() - startTime;\n document.getElementById('result-section').style.display = 'block';\n document.getElementById('file-name').textContent = file.name;\n document.getElementById('file-size').textContent = formatFileSize(file.size);\n document.getElementById('calc-time').textContent = calcTime + ' ms';\n document.getElementById('hash-value').textContent = '0x' + hashHex;\n document.getElementById('compare-result').style.display = 'none';\n document.getElementById('compare-hash').value = '';\n document.getElementById('result-section').scrollIntoView({ behavior: 'smooth' });\n } catch (error) { alert('❌ Error: ' + error.message); }\n}\n\nfunction copyHash() {\n const hashValue = document.getElementById('hash-value').textContent;\n navigator.clipboard.writeText(hashValue).then(() => { alert('Copied!'); });\n}\n\nfunction compareHash() {\n const calculatedHash = document.getElementById('hash-value').textContent.toLowerCase();\n let inputHash = document.getElementById('compare-hash').value.trim().toLowerCase();\n if (!inputHash) { alert('Please input hash'); return; }\n if (inputHash.startsWith('0x')) inputHash = inputHash.substring(2);\n const calculatedHashClean = calculatedHash.startsWith('0x') ? calculatedHash.substring(2) : calculatedHash;\n \n const resultDiv = document.getElementById('compare-result');\n resultDiv.style.display = 'block';\n \n if (calculatedHashClean === inputHash) {\n resultDiv.innerHTML = `\n
\n

✅ Verification Passed

\n

Hashes match perfectly. Data is intact.

\n
\n `;\n } else {\n resultDiv.innerHTML = `\n
\n

❌ Verification Failed

\n

Hash mismatch. Data might be modified.

\n
\n `;\n }\n}\n\nfunction formatFileSize(bytes) {\n if (bytes < 1024) return bytes + ' B';\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';\n return (bytes / (1024 * 1024)).toFixed(2) + ' MB';\n}"}}}}