| document.addEventListener('DOMContentLoaded', function() { |
| const searchButton = document.getElementById('searchButton'); |
| const searchQuery = document.getElementById('searchQuery'); |
| const databaseCheckboxes = document.querySelectorAll('input[name="database"]'); |
| let timeRange, sortBy; |
| |
| function getSelectedFilters() { |
| timeRange = document.querySelector('input[name="timeRange"]:checked').value; |
| sortBy = document.querySelector('input[name="sortBy"]:checked').value; |
| } |
| const loadingSpinner = document.getElementById('loadingSpinner'); |
| const resultsContainer = document.getElementById('resultsContainer'); |
| const noResults = document.getElementById('noResults'); |
| const resultCount = document.getElementById('resultCount'); |
| const articleList = document.getElementById('articleList'); |
| const constellation = d3.select('#constellation'); |
| const tooltip = document.getElementById('tooltip'); |
|
|
| |
| searchButton.addEventListener('click', performSearch); |
| searchQuery.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') performSearch(); |
| }); |
| function performSearch() { |
| const query = searchQuery.value.trim(); |
| if (!query) return; |
|
|
| getSelectedFilters(); |
| showLoading(true); |
| simulateAPICall(query); |
| } |
| function showLoading(show) { |
| loadingSpinner.classList.toggle('hidden', !show); |
| resultsContainer.classList.add('hidden'); |
| noResults.classList.add('hidden'); |
| } |
| function simulateAPICall(query) { |
| |
| setTimeout(() => { |
| try { |
| |
| const mockData = generateMockData(query); |
| console.log("Generated mock data:", mockData); |
| |
| |
| if (mockData.length > 0) { |
| displayResults(mockData); |
| } else { |
| |
| const fallbackData = generateMockData("médecine"); |
| displayResults(fallbackData); |
| } |
| } catch (error) { |
| console.error("Error displaying results:", error); |
| |
| const fallbackData = generateMockData("médecine"); |
| displayResults(fallbackData); |
| } finally { |
| showLoading(false); |
| } |
| }, 500); |
| } |
| function generateMockData(query) { |
| const yearsFilter = timeRange; |
| const count = 15 + Math.floor(Math.random() * 20); |
| const currentYear = new Date().getFullYear(); |
| |
| const articles = []; |
| const centralArticleCitations = 200 + Math.floor(Math.random() * 300); |
| |
| |
| articles.push({ |
| title: `L'étude la plus influente sur ${query} ces dernières années`, |
| authors: "Dupont, J.; Martin, A.; Leroy, K. et al.", |
| journal: "Nature Medicine", |
| year: currentYear - Math.floor(Math.random() * (yearsFilter === 'all' ? 20 : parseInt(yearsFilter))), |
| citations: centralArticleCitations, |
| doi: "10.1038/nm.1234", |
| abstract: `Cette étude révolutionnaire a transformé notre compréhension de ${query} en démontrant des améliorations significatives des résultats pour les patients. L'équipe de recherche a employé des méthodologies innovantes qui sont depuis devenues standard dans le domaine.` |
| }); |
| |
| |
| for (let i = 0; i < count - 1; i++) { |
| const citationCount = Math.floor(Math.random() * centralArticleCitations * 0.8); |
| |
| articles.push({ |
| title: `Étude ${i+1} sur ${query}: ${['Nouvelle', 'Complète', 'Systématique', 'Randomisée', 'Clinique'][i%5]} ${['découverte', 'analyse', 'étude', 'revue', 'évaluation'][i%5]}`, |
| authors: `${['Dubois', 'Bernard', 'Petit', 'Moreau', 'Lefevre'][i%5]}, ${String.fromCharCode(65 + (i%26))}. et al.`, |
| journal: ["La Presse Médicale", "NEJM", "The Lancet", "BMJ", "Annales"][i%5], |
| year: currentYear - Math.floor(Math.random() * (yearsFilter === 'all' ? 20 : parseInt(yearsFilter))), |
| citations: citationCount, |
| doi: `10.1234/abcd.${1000 + i}`, |
| abstract: `Cette étude ${['importante', 'fondamentale', 'innovante', 'détaillée', 'approfondie'][i%5]} a examiné ${query} à travers l'analyse ${['clinique', 'épidémiologique', 'moléculaire', 'biochimique', 'génomique'][i%5]}. Les résultats ont ${['confirmé', 'remis en question', 'élargi', 'affiné', 'redéfini'][i%5]} les connaissances précédentes dans ce domaine.` |
| }); |
| } |
| |
| return articles; |
| } |
|
|
| function displayResults(articles) { |
| |
| articles.sort((a, b) => b.citations - a.citations); |
| |
| |
| resultCount.textContent = `${articles.length} articles de recherche trouvés`; |
| |
| createConstellation(articles); |
| |
| |
| renderArticleList(articles); |
| |
| |
| resultsContainer.classList.remove('hidden'); |
| } |
|
|
| function createConstellation(articles) { |
| const width = constellation.node().clientWidth; |
| const height = constellation.node().clientHeight; |
| const center = { x: width / 2, y: height / 2 }; |
| |
| |
| constellation.selectAll("*").remove(); |
| |
| |
| const simulation = d3.forceSimulation() |
| .force("link", d3.forceLink().id(d => d.id).distance(100)) |
| .force("charge", d3.forceManyBody().strength(-100)) |
| .force("x", d3.forceX(center.x).strength(0.1)) |
| .force("y", d3.forceY(center.y).strength(0.1)); |
| |
| |
| const nodes = articles.map((article, i) => ({ |
| id: i, |
| ...article, |
| r: Math.sqrt(article.citations) / 3, |
| isCentral: i === 0 |
| })); |
| |
| |
| const links = nodes.slice(1).map(node => ({ |
| source: 0, |
| target: node.id, |
| value: node.citations / nodes[0].citations |
| })); |
| |
| |
| const link = constellation.append("g") |
| .selectAll("line") |
| .data(links) |
| .enter().append("line") |
| .attr("class", "link") |
| .attr("stroke-width", d => Math.sqrt(d.value) * 2); |
| |
| |
| const node = constellation.append("g") |
| .selectAll("circle") |
| .data(nodes) |
| .enter().append("circle") |
| .attr("class", d => `node ${d.isCentral ? 'central-node' : ''}`) |
| .attr("r", d => d.r) |
| .attr("fill", d => d.isCentral ? "#7c3aed" : "#4f46e5") |
| .on("mouseover", showTooltip) |
| .on("mouseout", hideTooltip) |
| .call(d3.drag() |
| .on("start", dragstarted) |
| .on("drag", dragged) |
| .on("end", dragended)); |
| |
| |
| constellation.append("g") |
| .selectAll("text") |
| .data(nodes.filter(d => d.isCentral)) |
| .enter().append("text") |
| .attr("dy", 4) |
| .attr("text-anchor", "middle") |
| .style("fill", "white") |
| .style("font-size", "10px") |
| .style("font-weight", "bold") |
| .text("★"); |
| |
| |
| simulation.nodes(nodes).on("tick", ticked); |
| simulation.force("link").links(links); |
| |
| function ticked() { |
| link |
| .attr("x1", d => d.source.x) |
| .attr("y1", d => d.source.y) |
| .attr("x2", d => d.target.x) |
| .attr("y2", d => d.target.y); |
| |
| node |
| .attr("cx", d => d.x) |
| .attr("cy", d => d.y); |
| } |
| |
| function dragstarted(event, d) { |
| if (!event.active) simulation.alphaTarget(0.3).restart(); |
| d.fx = d.x; |
| d.fy = d.y; |
| } |
| |
| function dragged(event, d) { |
| d.fx = event.x; |
| d.fy = event.y; |
| } |
| |
| function dragended(event, d) { |
| if (!event.active) simulation.alphaTarget(0); |
| d.fx = null; |
| d.fy = null; |
| } |
| } |
|
|
| function showTooltip(event, d) { |
| tooltip.innerHTML = ` |
| <h4 class="font-bold text-purple-700 mb-1">${d.title}</h4> |
| <p class="text-sm text-gray-600 mb-1">${d.authors}</p> |
| <p class="text-sm mb-2">${d.journal}, ${d.year} (Cited ${d.citations} times)</p> |
| <p class="text-xs text-gray-700 line-clamp-3">${d.abstract}</p> |
| `; |
| tooltip.style.left = `${event.pageX + 10}px`; |
| tooltip.style.top = `${event.pageY + 10}px`; |
| tooltip.classList.remove('hidden'); |
| } |
|
|
| function hideTooltip() { |
| tooltip.classList.add('hidden'); |
| } |
|
|
| function renderArticleList(articles) { |
| articleList.innerHTML = ''; |
| |
| articles.forEach((article, index) => { |
| const articleElement = document.createElement('div'); |
| articleElement.className = 'article-card bg-white rounded-lg shadow p-6'; |
| articleElement.innerHTML = ` |
| <div class="flex flex-col sm:flex-row gap-4"> |
| <div class="flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center ${index === 0 ? 'bg-purple-100 text-purple-700' : 'bg-blue-100 text-blue-700'}"> |
| <span class="font-bold">${index + 1}</span> |
| </div> |
| <div class="flex-grow"> |
| <h3 class="font-bold text-lg mb-1">${article.title}</h3> |
| <p class="text-sm text-gray-600 mb-1">${article.authors}</p> |
| <p class="text-sm text-gray-700 mb-2">${article.journal}, ${article.year} · Cited ${article.citations} times</p> |
| <p class="text-gray-700 text-sm line-clamp-2 mb-3">${article.abstract}</p> |
| <div class="flex flex-wrap gap-2"> |
| <a href="https://doi.org/${article.doi}" target="_blank" class="text-xs px-3 py-1 bg-gray-100 text-gray-700 rounded-full hover:bg-gray-200 transition-colors"> |
| Voir la publication |
| </a> |
| ${index === 0 ? ` |
| <span class="text-xs px-3 py-1 bg-purple-100 text-purple-700 rounded-full"> |
| Plus cité |
| </span> |
| ` : ''} |
| </div> |
| </div> |
| </div> |
| `; |
| articleList.appendChild(articleElement); |
| }); |
| } |
| function showNoResults() { |
| showLoading(false); |
| resultsContainer.classList.add('hidden'); |
| noResults.classList.remove('hidden'); |
| |
| |
| const suggestions = generateSearchSuggestions("médecine"); |
| |
| noResults.innerHTML = ` |
| <div class="text-center py-16"> |
| <i data-feather="alert-circle" class="mx-auto text-gray-400 w-16 h-16 mb-6"></i> |
| <h3 class="text-2xl font-bold text-gray-900 mb-3">Aucun résultat trouvé</h3> |
| <p class="text-gray-600 text-lg">Essayez ces recherches alternatives :</p> |
| <div class="mt-6 flex flex-wrap justify-center gap-2"> |
| ${suggestions.map(s => ` |
| <button onclick="document.getElementById('searchQuery').value='${s}'; performSearch();" |
| class="text-sm px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"> |
| ${s} |
| </button> |
| `).join('')} |
| </div> |
| <button onclick="performSearch()" class="mt-8 px-6 py-3 bg-white text-gray-700 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors font-medium"> |
| Réessayer |
| </button> |
| </div> |
| `; |
| feather.replace(); |
| } |
| function generateSearchSuggestions(query) { |
| const commonTerms = { |
| 'cancer': ['cancer du sein', 'cancer du poumon', 'traitement du cancer', 'thérapie ciblée cancer'], |
| 'diabète': ['diabète type 1', 'diabète type 2', 'traitement diabète', 'prévention diabète'], |
| 'covid': ['covid-19', 'vaccin covid', 'variants covid', 'traitement covid'] |
| }; |
| |
| |
| for (const [term, suggestions] of Object.entries(commonTerms)) { |
| if (query.toLowerCase().includes(term)) { |
| return suggestions; |
| } |
| } |
| |
| |
| return [ |
| query + ' traitement', |
| query + ' étude clinique', |
| 'nouveautés ' + query, |
| 'méta-analyse ' + query |
| ]; |
| } |
| }); |