File size: 13,648 Bytes
2e92a32 63486ed 7e017a8 63486ed 2e92a32 7e017a8 2e92a32 7e017a8 2e92a32 7e017a8 70f9f27 0749f0a 7e017a8 0749f0a 7e017a8 2e92a32 0749f0a 2e92a32 70f9f27 7e017a8 2e92a32 1b3073a 2e92a32 1b3073a 2e92a32 1b3073a 2e92a32 1b3073a 2e92a32 1b3073a 2e92a32 1b3073a 2e92a32 1b3073a 2e92a32 7e017a8 577353d 0749f0a 70f9f27 0749f0a 70f9f27 0749f0a 577353d 70f9f27 577353d 2e92a32 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
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');
// Search function
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) {
// In a real implementation, this would call actual APIs
setTimeout(() => {
try {
// Always generate results for debugging
const mockData = generateMockData(query);
console.log("Generated mock data:", mockData);
// Force display results even with empty query for testing
if (mockData.length > 0) {
displayResults(mockData);
} else {
// If no mock data, still show something for testing
const fallbackData = generateMockData("médecine");
displayResults(fallbackData);
}
} catch (error) {
console.error("Error displaying results:", error);
// Show fallback results instead of no results
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);
// Generate central article
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.`
});
// Generate surrounding articles
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) {
// Sort articles by citations
articles.sort((a, b) => b.citations - a.citations);
// Update result count
resultCount.textContent = `${articles.length} articles de recherche trouvés`;
// Create constellation visualization
createConstellation(articles);
// Create article list
renderArticleList(articles);
// Show results
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 };
// Clear previous visualization
constellation.selectAll("*").remove();
// Create simulation
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));
// Prepare nodes data
const nodes = articles.map((article, i) => ({
id: i,
...article,
r: Math.sqrt(article.citations) / 3,
isCentral: i === 0
}));
// Create links (central node connects to all others)
const links = nodes.slice(1).map(node => ({
source: 0,
target: node.id,
value: node.citations / nodes[0].citations
}));
// Draw links
const link = constellation.append("g")
.selectAll("line")
.data(links)
.enter().append("line")
.attr("class", "link")
.attr("stroke-width", d => Math.sqrt(d.value) * 2);
// Draw nodes
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));
// Add node labels (central node only)
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("★");
// Update simulation
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');
// Generate fallback suggestions
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']
};
// Check if query matches any common term
for (const [term, suggestions] of Object.entries(commonTerms)) {
if (query.toLowerCase().includes(term)) {
return suggestions;
}
}
// Default suggestions
return [
query + ' traitement',
query + ' étude clinique',
'nouveautés ' + query,
'méta-analyse ' + query
];
}
}); |