// DOM elements 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'); const sectionPopup = document.getElementById('sectionPopup'); const popupTitle = document.getElementById('popupTitle'); const popupTextareas = document.getElementById('popupTextareas'); const copyAllBtn = document.getElementById('copyAllBtn'); const closePopupBtn = document.querySelector('.close-popup'); // Search mode toggle 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{ let body = { keywords, "case_sensitive": caseSensitiveFilter.checked, "mode": modeFilter.value }; if (releaseFilter.value != ""){body.release = releaseFilter.value} if (workingGroupFilter.value != ""){body["working_group"] = workingGroupFilter.value} if (specTypeFilter.value != ""){body["spec_type"] = specTypeFilter.value} const response = await fetch("/search-spec", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }); 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(); } }) // Single document search 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(); } }); // Batch document search 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(); } }); // Display single result function displaySingleResult(data) { resultsList.innerHTML = ''; const resultItem = document.createElement('div'); resultItem.className = 'result-item'; let scopeItem = data.scope ? `

Scope : ${data.scope}

` : "" resultItem.innerHTML = `
${data.doc_id}
Found
${data.url} ${scopeItem}
`; resultsList.appendChild(resultItem); resultsStats.textContent = `Found in ${data.search_time.toFixed(2)} seconds`; resultsContainer.style.display = 'block'; } // Display single not found result function displaySingleNotFound(docId, message) { resultsList.innerHTML = ''; const resultItem = document.createElement('div'); resultItem.className = 'result-item'; resultItem.innerHTML = `
${docId}
Not Found
${message}
`; 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 = `
${spec.id}
Found

Title: ${spec.title}

Type: ${spec.type}

Release: ${spec.release}

Version: ${spec.version}

WG: ${spec.working_group}

URL: ${spec.url}

Scope: ${spec.scope}

`; // Ajouter le bouton au DOM resultsList.appendChild(resultItem); // Récupérer le bouton nouvellement créé const button1 = resultItem.querySelector('.get-section-btn'); // Stocker l'objet directement sur l'élément DOM button1._sections = spec.contains; }); document.querySelectorAll('.get-section-btn').forEach(button => { button.addEventListener('click', function() { let specId = this.getAttribute("data-spec-id"); let sections = this._sections; openSectionPopup(specId, sections); }); }); resultsStats.textContent = `Found ${data.results.length} in ${data.search_time.toFixed(2)} seconds` resultsContainer.style.display = 'block'; } function openSectionPopup(specId, sections) { popupTitle.textContent = `Sections of specification ${specId}`; popupTextareas.innerHTML = ''; Object.entries(sections).forEach(([section, content], index) => { const container = document.createElement("div"); container.className = "textarea-container"; const textarea = document.createElement("textarea"); textarea.id = `section-${index}`; textarea.value = `${section}\n\n${content}` textarea.readOnly = true; const copyBtn = document.createElement('button'); copyBtn.className = 'copy-btn'; copyBtn.textContent = 'Copy'; copyBtn.onclick = () => copyTextarea(`section-${index}`); container.appendChild(textarea); container.appendChild(copyBtn); popupTextareas.appendChild(container); }); sectionPopup.style.display = 'block'; document.body.style.overflow = 'hidden'; } function copyTextarea(id) { const textarea = document.getElementById(id); textarea.select(); document.execCommand("copy"); // Effet visuel pour confirmer la copie const btn = textarea.nextElementSibling; const originalText = btn.textContent; btn.textContent = 'Copied !'; btn.style.backgroundColor = '#34a853'; btn.style.color = 'white'; setTimeout(() => { btn.textContent = originalText; btn.style.backgroundColor = ''; btn.style.color = ''; }, 1500); } // Fonction pour copier tout le contenu copyAllBtn.addEventListener('click', () => { const textareas = popupTextareas.querySelectorAll('textarea'); let allContent = ''; textareas.forEach((textarea, index) => { allContent += textarea.value; if (index < textareas.length - 1) { allContent += '\n\n---\n\n'; } }); // Créer un textarea temporaire pour copier le contenu const tempTextarea = document.createElement('textarea'); tempTextarea.value = allContent; document.body.appendChild(tempTextarea); tempTextarea.select(); document.execCommand('copy'); document.body.removeChild(tempTextarea); // Effet visuel pour confirmer la copie const originalText = copyAllBtn.textContent; copyAllBtn.textContent = 'Copied all !'; setTimeout(() => { copyAllBtn.textContent = originalText; }, 1500); }); // Fermer la popup closePopupBtn.addEventListener('click', () => { sectionPopup.style.display = 'none'; document.body.style.overflow = ''; // Rétablir le défilement du body }); // Fermer la popup en cliquant à l'extérieur window.addEventListener('click', (event) => { if (event.target === sectionPopup) { sectionPopup.style.display = 'none'; document.body.style.overflow = ''; } }); // Display batch results function displayBatchResults(data) { resultsList.innerHTML = ''; // Found documents Object.entries(data.results).forEach(([docId, url]) => { const resultItem = document.createElement('div'); resultItem.className = 'result-item'; resultItem.innerHTML = `
${docId}
Found
${url}
`; resultsList.appendChild(resultItem); }); // Not found documents data.missing.forEach(docId => { const resultItem = document.createElement('div'); resultItem.className = 'result-item'; resultItem.innerHTML = `
${docId}
Not Found
`; 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'; } // Show loader function showLoader() { loader.style.display = 'block'; } // Hide loader function hideLoader() { loader.style.display = 'none'; } // Show error message function showError(message) { errorMessage.textContent = message; errorMessage.style.display = 'block'; } // Hide error message function hideError() { errorMessage.style.display = 'none'; } // Enter key event for single search docIdInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { searchBtn.click(); } }); keywordInput.addEventListener('keypress', (event)=>{ if (event.key === "Enter"){ keywordSearchBtn.click(); } })