|
|
|
const dynamicDesc = document.getElementById("dynamicDesc"); |
|
const dynamicTitle = document.getElementById("dynamicTitle"); |
|
|
|
const singleModeBtn = document.getElementById('single-mode-btn'); |
|
const batchModeBtn = document.getElementById('batch-mode-btn'); |
|
const keywordModeBtn = document.getElementById("keyword-mode-btn"); |
|
|
|
const singleInput = document.querySelector('.single-input'); |
|
const batchInput = document.querySelector('.batch-input'); |
|
const keywordSearchInput = document.querySelector(".keyword-input"); |
|
|
|
const docIdInput = document.getElementById('doc-id'); |
|
const batchIdsInput = document.getElementById('batch-ids'); |
|
const keywordInput = document.getElementById("keywords"); |
|
|
|
const releaseFilter = document.querySelector("input[name=release]") |
|
const modeFilter = document.querySelector("select[name=mode]") |
|
const specTypeFilter = document.querySelector("select[name=spec_type]") |
|
const workingGroupFilter = document.querySelector("select[name=working_group]") |
|
const caseSensitiveFilter = document.querySelector("input[name=case_sensitive]") |
|
|
|
const searchBtn = document.getElementById('search-btn'); |
|
const batchSearchBtn = document.getElementById('batch-search-btn'); |
|
const keywordSearchBtn = document.getElementById("keyword-search-btn"); |
|
|
|
const loader = document.getElementById('loader'); |
|
const resultsContainer = document.getElementById('results-container'); |
|
const resultsList = document.getElementById('results-list'); |
|
const resultsStats = document.getElementById('results-stats'); |
|
const errorMessage = document.getElementById('error-message'); |
|
|
|
|
|
singleModeBtn.addEventListener('click', () => { |
|
dynamicTitle.textContent = "Find 3GPP Documents"; |
|
dynamicDesc.textContent = "Enter a TSG document ID / specification ID (e.g., S1-123456, C2-987654 or 31.102) to locate the document in the 3GPP FTP server."; |
|
|
|
singleModeBtn.classList.add('active'); |
|
keywordModeBtn.classList.remove("active"); |
|
batchModeBtn.classList.remove('active'); |
|
|
|
singleInput.style.display = 'block'; |
|
batchInput.style.display = 'none'; |
|
keywordSearchInput.style.display = "none"; |
|
}); |
|
|
|
batchModeBtn.addEventListener('click', () => { |
|
dynamicTitle.textContent = "Find multiple 3GPP Documents"; |
|
dynamicDesc.textContent = "Enter a list of TSG document ID / specification ID (e.g., S1-123456, C2-987654 or 31.102) to locate all of the specified documents in the 3GPP FTP server."; |
|
|
|
batchModeBtn.classList.add('active'); |
|
keywordModeBtn.classList.remove("active"); |
|
singleModeBtn.classList.remove('active'); |
|
|
|
batchInput.style.display = 'block'; |
|
keywordSearchInput.style.display = "none"; |
|
singleInput.style.display = 'none'; |
|
}); |
|
|
|
keywordModeBtn.addEventListener('click', () => { |
|
dynamicTitle.textContent = "Search 3GPP specifications"; |
|
dynamicDesc.textContent = "With keywords and filters, find all of 3GPP's specifications that matches your needs (with keywords, specification number, release or even working group (C1, S5, SP, CP: always the first letter of the group followed by the workgroup number)"; |
|
|
|
keywordModeBtn.classList.add("active"); |
|
singleModeBtn.classList.remove('active'); |
|
batchModeBtn.classList.remove("active"); |
|
|
|
singleInput.style.display = "none"; |
|
batchInput.style.display = "none"; |
|
keywordSearchInput.style.display = "block"; |
|
}) |
|
|
|
document.getElementById('toggleFilters').onclick = function() { |
|
var target = document.getElementById('filtersForm'); |
|
target.style.display = (target.style.display === 'none' || target.style.display === '') ? 'flex' : 'none'; |
|
}; |
|
|
|
keywordSearchBtn.addEventListener("click", async ()=>{ |
|
const keywords = keywordInput.value.trim(); |
|
if (!keywords) { |
|
showError("Please enter at least one keyword"); |
|
return; |
|
} |
|
|
|
showLoader(); |
|
hideError(); |
|
|
|
try{ |
|
const response = await fetch("/search-spec", { |
|
method: "POST", |
|
headers: { |
|
"Content-Type": "application/json" |
|
}, |
|
body: JSON.stringify({ |
|
keywords, |
|
"case_sensitive": caseSensitiveFilter.checked, |
|
"release": releaseFilter.value != '' ? releaseFilter.value : null, |
|
"mode": modeFilter.value, |
|
"working_group": workingGroupFilter != '' ? workingGroupFilter.value : null, |
|
"spec_type": specTypeFilter.value |
|
}) |
|
}); |
|
|
|
const data = await response.json(); |
|
if (response.ok){ |
|
displayKeywordResults(data); |
|
} else { |
|
showError('Error processing batch request'); |
|
} |
|
} catch (error) { |
|
showError('Error connecting to the server. Please check if the API is running.'); |
|
console.error('Error:', error); |
|
} finally { |
|
hideLoader(); |
|
} |
|
}) |
|
|
|
|
|
|
|
searchBtn.addEventListener('click', async () => { |
|
const docId = docIdInput.value.trim(); |
|
if (!docId) { |
|
showError('Please enter a document ID'); |
|
return; |
|
} |
|
|
|
showLoader(); |
|
hideError(); |
|
|
|
try { |
|
const response = await fetch(`/find`, { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: JSON.stringify({ doc_id: docId, release: null }) |
|
}); |
|
|
|
const data = await response.json(); |
|
|
|
if (response.ok) { |
|
displaySingleResult(data); |
|
} else { |
|
displaySingleNotFound(docId, data.detail); |
|
} |
|
} catch (error) { |
|
showError('Error connecting to the server. Please check if the API is running.'); |
|
console.error('Error:', error); |
|
} finally { |
|
hideLoader(); |
|
} |
|
}); |
|
|
|
|
|
batchSearchBtn.addEventListener('click', async () => { |
|
const batchText = batchIdsInput.value.trim(); |
|
if (!batchText) { |
|
showError('Please enter at least one document ID'); |
|
return; |
|
} |
|
|
|
const docIds = batchText.split('\n') |
|
.map(id => id.trim()) |
|
.filter(id => id !== ''); |
|
|
|
if (docIds.length === 0) { |
|
showError('Please enter at least one valid document ID'); |
|
return; |
|
} |
|
|
|
showLoader(); |
|
hideError(); |
|
|
|
try { |
|
const response = await fetch(`/batch`, { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: JSON.stringify({ doc_ids: docIds }) |
|
}); |
|
|
|
const data = await response.json(); |
|
|
|
if (response.ok) { |
|
displayBatchResults(data); |
|
} else { |
|
showError('Error processing batch request'); |
|
} |
|
} catch (error) { |
|
showError('Error connecting to the server. Please check if the API is running.'); |
|
console.error('Error:', error); |
|
} finally { |
|
hideLoader(); |
|
} |
|
}); |
|
|
|
|
|
function displaySingleResult(data) { |
|
resultsList.innerHTML = ''; |
|
|
|
const resultItem = document.createElement('div'); |
|
resultItem.className = 'result-item'; |
|
let scopeItem = data.scope ? `<p>Scope : ${data.scope}</p>` : "" |
|
resultItem.innerHTML = ` |
|
<div class="result-header"> |
|
<div class="result-id">${data.doc_id}</div> |
|
<div class="result-status status-found">Found</div> |
|
</div> |
|
<div class="result-url"> |
|
<a href="${data.url}" target="_blank">${data.url}</a> |
|
${scopeItem} |
|
</div> |
|
`; |
|
|
|
resultsList.appendChild(resultItem); |
|
resultsStats.textContent = `Found in ${data.search_time.toFixed(2)} seconds`; |
|
resultsContainer.style.display = 'block'; |
|
} |
|
|
|
|
|
function displaySingleNotFound(docId, message) { |
|
resultsList.innerHTML = ''; |
|
|
|
const resultItem = document.createElement('div'); |
|
resultItem.className = 'result-item'; |
|
resultItem.innerHTML = ` |
|
<div class="result-header"> |
|
<div class="result-id">${docId}</div> |
|
<div class="result-status status-not-found">Not Found</div> |
|
</div> |
|
<div>${message}</div> |
|
`; |
|
|
|
resultsList.appendChild(resultItem); |
|
resultsStats.textContent = 'Document not found'; |
|
resultsContainer.style.display = 'block'; |
|
} |
|
|
|
function displayKeywordResults(data) { |
|
resultsList.innerHTML = ''; |
|
|
|
data.results.forEach(spec => { |
|
const resultItem = document.createElement("div"); |
|
resultItem.className = "result-item" |
|
resultItem.innerHTML = ` |
|
<div class="result-header"> |
|
<div class="result-id">${spec.id}</div> |
|
<div class="result-status status-found">Found</div> |
|
</div> |
|
<div class="result-url"> |
|
<p>Title: ${spec.title}</p> |
|
<p>Type: ${spec.type}</p> |
|
<p>Release: ${spec.release}</p> |
|
<p>Version: ${spec.version}</p> |
|
<p>WG: ${spec.working_group}</p> |
|
<p>URL: <a target="_blank" href="${spec.url}">${spec.url}</a></p> |
|
<p>Scope: ${spec.scope}</p> |
|
</div> |
|
`; |
|
resultsList.appendChild(resultItem); |
|
}); |
|
resultsStats.textContent = `Found ${data.results.length} in ${data.search_time.toFixed(2)} seconds` |
|
resultsContainer.style.display = 'block'; |
|
} |
|
|
|
|
|
function displayBatchResults(data) { |
|
resultsList.innerHTML = ''; |
|
|
|
|
|
Object.entries(data.results).forEach(([docId, url]) => { |
|
const resultItem = document.createElement('div'); |
|
resultItem.className = 'result-item'; |
|
resultItem.innerHTML = ` |
|
<div class="result-header"> |
|
<div class="result-id">${docId}</div> |
|
<div class="result-status status-found">Found</div> |
|
</div> |
|
<div class="result-url"> |
|
<a href="${url}" target="_blank">${url}</a> |
|
</div> |
|
`; |
|
resultsList.appendChild(resultItem); |
|
}); |
|
|
|
|
|
data.missing.forEach(docId => { |
|
const resultItem = document.createElement('div'); |
|
resultItem.className = 'result-item'; |
|
resultItem.innerHTML = ` |
|
<div class="result-header"> |
|
<div class="result-id">${docId}</div> |
|
<div class="result-status status-not-found">Not Found</div> |
|
</div> |
|
`; |
|
resultsList.appendChild(resultItem); |
|
}); |
|
|
|
const foundCount = Object.keys(data.results).length; |
|
const totalCount = foundCount + data.missing.length; |
|
|
|
resultsStats.textContent = `Found ${foundCount} of ${totalCount} documents in ${data.search_time.toFixed(2)} seconds`; |
|
resultsContainer.style.display = 'block'; |
|
} |
|
|
|
|
|
function showLoader() { |
|
loader.style.display = 'block'; |
|
} |
|
|
|
|
|
function hideLoader() { |
|
loader.style.display = 'none'; |
|
} |
|
|
|
|
|
function showError(message) { |
|
errorMessage.textContent = message; |
|
errorMessage.style.display = 'block'; |
|
} |
|
|
|
|
|
function hideError() { |
|
errorMessage.style.display = 'none'; |
|
} |
|
|
|
|
|
docIdInput.addEventListener('keypress', (e) => { |
|
if (e.key === 'Enter') { |
|
searchBtn.click(); |
|
} |
|
}); |
|
|
|
keywordInput.addEventListener('keypress', (event)=>{ |
|
if (event.key === "Enter"){ |
|
keywordSearchBtn.click(); |
|
} |
|
}) |