| <!DOCTYPE html> |
| <html lang="fr"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Devine l'Insecte | Jeu Géographique</title> |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> |
| <link href="https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;600;700&family=Source+Sans+3:wght@300;400;600&display=swap" rel="stylesheet"> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| :root { |
| --sepia: #f4f1e8; |
| --dark-sepia: #3d3526; |
| --accent-green: #4a7c59; |
| --accent-gold: #d4a574; |
| --shadow: rgba(61, 53, 38, 0.15); |
| --border: rgba(61, 53, 38, 0.2); |
| } |
| |
| body { |
| font-family: 'Source Sans 3', sans-serif; |
| background: linear-gradient(135deg, #f4f1e8 0%, #e8e3d6 100%); |
| color: var(--dark-sepia); |
| overflow-x: hidden; |
| min-height: 100vh; |
| } |
| |
| body::before { |
| content: ''; |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-image: |
| repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(61, 53, 38, 0.03) 2px, rgba(61, 53, 38, 0.03) 4px), |
| repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(61, 53, 38, 0.03) 2px, rgba(61, 53, 38, 0.03) 4px); |
| pointer-events: none; |
| z-index: 1; |
| opacity: 0.3; |
| } |
| |
| .container { |
| max-width: 1400px; |
| margin: 0 auto; |
| padding: 2rem; |
| position: relative; |
| z-index: 2; |
| } |
| |
| header { |
| text-align: center; |
| margin-bottom: 3rem; |
| animation: fadeInDown 1s ease; |
| } |
| |
| h1 { |
| font-family: 'Crimson Pro', serif; |
| font-size: 3.5rem; |
| font-weight: 700; |
| color: var(--accent-green); |
| letter-spacing: -0.02em; |
| margin-bottom: 0.5rem; |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.1); |
| } |
| |
| .subtitle { |
| font-size: 1.1rem; |
| color: var(--dark-sepia); |
| opacity: 0.8; |
| font-style: italic; |
| } |
| |
| .stats-bar { |
| display: flex; |
| justify-content: center; |
| gap: 2rem; |
| margin-top: 1rem; |
| font-size: 0.95rem; |
| color: var(--dark-sepia); |
| opacity: 0.7; |
| } |
| |
| .game-container { |
| display: grid; |
| grid-template-columns: 1fr 400px; |
| gap: 2rem; |
| animation: fadeInUp 1s ease 0.2s both; |
| } |
| |
| .map-panel { |
| background: white; |
| border-radius: 16px; |
| box-shadow: |
| 0 10px 40px var(--shadow), |
| inset 0 0 0 1px var(--border); |
| overflow: hidden; |
| position: relative; |
| } |
| |
| .map-header { |
| background: linear-gradient(135deg, var(--accent-green) 0%, #5a8c69 100%); |
| color: white; |
| padding: 1.5rem 2rem; |
| font-family: 'Crimson Pro', serif; |
| font-size: 1.4rem; |
| font-weight: 600; |
| letter-spacing: 0.02em; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .map-controls { |
| display: flex; |
| gap: 0.5rem; |
| } |
| |
| .map-type-btn { |
| background: rgba(255, 255, 255, 0.2); |
| border: 2px solid rgba(255, 255, 255, 0.3); |
| color: white; |
| padding: 0.5rem 1rem; |
| border-radius: 6px; |
| font-family: 'Source Sans 3', sans-serif; |
| font-size: 0.85rem; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| } |
| |
| .map-type-btn:hover { |
| background: rgba(255, 255, 255, 0.3); |
| border-color: rgba(255, 255, 255, 0.5); |
| } |
| |
| .map-type-btn.active { |
| background: white; |
| color: var(--accent-green); |
| border-color: white; |
| } |
| |
| #map { |
| height: 600px; |
| width: 100%; |
| background: #a8c5d9; |
| } |
| |
| .control-panel { |
| background: white; |
| border-radius: 16px; |
| box-shadow: |
| 0 10px 40px var(--shadow), |
| inset 0 0 0 1px var(--border); |
| padding: 2rem; |
| display: flex; |
| flex-direction: column; |
| gap: 1.5rem; |
| max-height: 800px; |
| overflow-y: auto; |
| } |
| |
| .score-display { |
| background: linear-gradient(135deg, #f8f6f0 0%, #ede9dc 100%); |
| padding: 1.5rem; |
| border-radius: 10px; |
| text-align: center; |
| border: 2px solid var(--border); |
| position: sticky; |
| top: 0; |
| z-index: 10; |
| } |
| |
| .score-label { |
| font-family: 'Crimson Pro', serif; |
| font-size: 0.9rem; |
| color: var(--dark-sepia); |
| opacity: 0.7; |
| text-transform: uppercase; |
| letter-spacing: 0.1em; |
| margin-bottom: 0.5rem; |
| } |
| |
| .score-value { |
| font-size: 2.5rem; |
| font-weight: 700; |
| color: var(--accent-green); |
| font-family: 'Crimson Pro', serif; |
| } |
| |
| .answer-input-section { |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| } |
| |
| .answer-input-section label { |
| font-family: 'Crimson Pro', serif; |
| font-size: 1.1rem; |
| font-weight: 600; |
| color: var(--accent-green); |
| } |
| |
| .input-wrapper { |
| position: relative; |
| } |
| |
| #species-input { |
| width: 100%; |
| padding: 1rem; |
| border: 2px solid var(--border); |
| border-radius: 10px; |
| font-family: 'Crimson Pro', serif; |
| font-size: 1rem; |
| background: var(--sepia); |
| color: var(--dark-sepia); |
| transition: all 0.3s ease; |
| } |
| |
| #species-input:focus { |
| outline: none; |
| border-color: var(--accent-green); |
| background: white; |
| box-shadow: 0 4px 12px var(--shadow); |
| } |
| |
| .autocomplete-dropdown { |
| position: absolute; |
| top: 100%; |
| left: 0; |
| right: 0; |
| background: white; |
| border: 2px solid var(--accent-green); |
| border-top: none; |
| border-radius: 0 0 10px 10px; |
| max-height: 300px; |
| overflow-y: auto; |
| z-index: 1000; |
| box-shadow: 0 8px 16px var(--shadow); |
| } |
| |
| .autocomplete-item { |
| padding: 0.75rem 1rem; |
| cursor: pointer; |
| transition: background 0.2s ease; |
| border-bottom: 1px solid var(--border); |
| } |
| |
| .autocomplete-item:last-child { |
| border-bottom: none; |
| } |
| |
| .autocomplete-item:hover, |
| .autocomplete-item.selected { |
| background: var(--sepia); |
| } |
| |
| .autocomplete-item strong { |
| color: var(--accent-green); |
| display: block; |
| margin-bottom: 0.25rem; |
| } |
| |
| .autocomplete-item em { |
| color: var(--dark-sepia); |
| opacity: 0.7; |
| font-size: 0.9rem; |
| } |
| |
| .submit-btn { |
| background: linear-gradient(135deg, var(--accent-green) 0%, #5a8c69 100%); |
| color: white; |
| border: none; |
| padding: 1rem 1.5rem; |
| border-radius: 10px; |
| font-family: 'Crimson Pro', serif; |
| font-size: 1.1rem; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| box-shadow: 0 4px 12px rgba(74, 124, 89, 0.3); |
| } |
| |
| .submit-btn:hover:not(:disabled) { |
| transform: translateY(-2px); |
| box-shadow: 0 6px 20px rgba(74, 124, 89, 0.4); |
| } |
| |
| .submit-btn:disabled { |
| opacity: 0.5; |
| cursor: not-allowed; |
| } |
| |
| .hints-section { |
| border-top: 2px solid var(--border); |
| padding-top: 1.5rem; |
| } |
| |
| .hint-btn { |
| background: linear-gradient(135deg, var(--accent-gold) 0%, #c49564 100%); |
| color: white; |
| border: none; |
| padding: 1rem 1.5rem; |
| border-radius: 10px; |
| font-family: 'Crimson Pro', serif; |
| font-size: 1.1rem; |
| font-weight: 600; |
| cursor: pointer; |
| width: 100%; |
| transition: all 0.3s ease; |
| box-shadow: 0 4px 12px rgba(212, 165, 116, 0.3); |
| } |
| |
| .hint-btn:hover:not(:disabled) { |
| transform: translateY(-2px); |
| box-shadow: 0 6px 20px rgba(212, 165, 116, 0.4); |
| } |
| |
| .hint-btn:disabled { |
| opacity: 0.5; |
| cursor: not-allowed; |
| } |
| |
| .hints-display { |
| margin-top: 1rem; |
| display: flex; |
| flex-direction: column; |
| gap: 0.75rem; |
| } |
| |
| .hint-item { |
| background: #fff9f0; |
| border-left: 4px solid var(--accent-gold); |
| padding: 1rem; |
| border-radius: 6px; |
| font-size: 0.95rem; |
| line-height: 1.6; |
| animation: slideInRight 0.4s ease; |
| } |
| |
| .hint-label { |
| font-weight: 600; |
| color: var(--accent-green); |
| margin-bottom: 0.25rem; |
| } |
| |
| .next-btn { |
| background: var(--accent-green); |
| color: white; |
| border: none; |
| padding: 1rem 1.5rem; |
| border-radius: 10px; |
| font-family: 'Crimson Pro', serif; |
| font-size: 1.1rem; |
| font-weight: 600; |
| cursor: pointer; |
| width: 100%; |
| transition: all 0.3s ease; |
| box-shadow: 0 4px 12px rgba(74, 124, 89, 0.3); |
| } |
| |
| .next-btn:hover { |
| background: #5a8c69; |
| transform: translateY(-2px); |
| box-shadow: 0 6px 20px rgba(74, 124, 89, 0.4); |
| } |
| |
| .result-message { |
| padding: 1.5rem; |
| border-radius: 10px; |
| text-align: center; |
| font-family: 'Crimson Pro', serif; |
| font-size: 1.2rem; |
| font-weight: 600; |
| animation: fadeIn 0.5s ease; |
| } |
| |
| .result-message.correct { |
| background: rgba(74, 124, 89, 0.1); |
| color: var(--accent-green); |
| border: 2px solid var(--accent-green); |
| } |
| |
| .result-message.incorrect { |
| background: rgba(197, 74, 74, 0.1); |
| color: #c54a4a; |
| border: 2px solid #c54a4a; |
| } |
| |
| @keyframes fadeInDown { |
| from { |
| opacity: 0; |
| transform: translateY(-30px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
| |
| @keyframes fadeInUp { |
| from { |
| opacity: 0; |
| transform: translateY(30px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
| |
| @keyframes slideInRight { |
| from { |
| opacity: 0; |
| transform: translateX(-20px); |
| } |
| to { |
| opacity: 1; |
| transform: translateX(0); |
| } |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| |
| @media (max-width: 1024px) { |
| .game-container { |
| grid-template-columns: 1fr; |
| } |
| |
| h1 { |
| font-size: 2.5rem; |
| } |
| |
| #map { |
| height: 500px; |
| } |
| } |
| |
| .leaflet-control-attribution { |
| background: rgba(255, 255, 255, 0.8) !important; |
| font-size: 0.7rem !important; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <header> |
| <h1>🦗 Devine l'Insecte</h1> |
| <p class="subtitle">Identifie l'espèce selon sa distribution géographique</p> |
| <div class="stats-bar"> |
| <span>📊 <span id="total-species">22</span> espèces disponibles</span> |
| </div> |
| </header> |
|
|
| <div class="game-container"> |
| <div class="map-panel"> |
| <div class="map-header"> |
| <span>Distribution Mondiale des Occurrences</span> |
| <div class="map-controls"> |
| <button class="map-type-btn active" data-map="standard">Standard</button> |
| <button class="map-type-btn" data-map="dark">Sombre</button> |
| <button class="map-type-btn" data-map="satellite">Satellite</button> |
| </div> |
| </div> |
| <div id="map"></div> |
| </div> |
|
|
| <div class="control-panel"> |
| <div class="score-display"> |
| <div class="score-label">Score</div> |
| <div class="score-value" id="score">0</div> |
| </div> |
|
|
| <div id="result-container"></div> |
|
|
| <div class="answer-input-section"> |
| <label for="species-input">Quelle est cette espèce ?</label> |
| <div class="input-wrapper"> |
| <input |
| type="text" |
| id="species-input" |
| placeholder="Tapez le nom de l'espèce..." |
| autocomplete="off" |
| /> |
| <div id="autocomplete-dropdown" class="autocomplete-dropdown" style="display: none;"></div> |
| </div> |
| <button class="submit-btn" id="submit-btn"> |
| Valider ma réponse |
| </button> |
| </div> |
|
|
| <div class="hints-section"> |
| <button class="hint-btn" id="hint-btn"> |
| 💡 Demander un indice |
| </button> |
| <div class="hints-display" id="hints-display"></div> |
| </div> |
|
|
| <button class="next-btn" id="next-btn" style="display: none;"> |
| Mante Suivante → |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> |
| <script> |
| |
| const speciesData = [ |
| { |
| name: "Phasma gigas", |
| commonName: "Phasme géant", |
| taxonKey: "5049821", |
| family: "Phasmatidae", |
| region: "Asie du Sud-Est", |
| hints: [ |
| "Famille : Phasmatidae", |
| "Distribution : Asie du Sud-Est", |
| "Un des plus grands phasmes au monde, peut atteindre 30 cm de longueur" |
| ] |
| }, |
| { |
| name: "Macromantis hyalina", |
| commonName: "Mante hyaline", |
| taxonKey: "1405821", |
| family: "Mantidae", |
| region: "Amérique du Sud", |
| hints: [ |
| "Famille : Mantidae", |
| "Distribution : Amérique du Sud", |
| "Grande mante aux ailes transparentes, typique des forêts tropicales" |
| ] |
| }, |
| { |
| name: "Blepharopsis mendica", |
| commonName: "Mante diable", |
| taxonKey: "1406697", |
| family: "Empusidae", |
| region: "Afrique du Nord, Moyen-Orient", |
| hints: [ |
| "Famille : Empusidae", |
| "Distribution : Afrique du Nord et Moyen-Orient", |
| "Mante du désert avec des yeux spectaculaires et des motifs colorés" |
| ] |
| }, |
| { |
| name: "Creobroter gemmatus", |
| commonName: "Mante fleur indienne", |
| taxonKey: "1406395", |
| family: "Hymenopodidae", |
| region: "Asie du Sud", |
| hints: [ |
| "Famille : Hymenopodidae", |
| "Distribution : Inde, Asie du Sud", |
| "Petite mante colorée avec taches oculaires vertes sur les ailes" |
| ] |
| }, |
| { |
| name: "Hymenopus coronatus", |
| commonName: "Mante orchidée", |
| taxonKey: "1406471", |
| family: "Hymenopodidae", |
| region: "Asie du Sud-Est", |
| hints: [ |
| "Famille : Hymenopodidae", |
| "Distribution : Asie du Sud-Est (Malaisie, Indonésie)", |
| "Célèbre pour son mimétisme d'orchidée, une des plus belles mantes au monde" |
| ] |
| }, |
| { |
| name: "Callibia diana", |
| commonName: "Mante de Diana", |
| taxonKey: "1404244", |
| family: "Nanomantidae", |
| region: "Afrique", |
| hints: [ |
| "Famille : Nanomantidae", |
| "Distribution : Afrique tropicale", |
| "Petite mante aux motifs contrastés noir et blanc" |
| ] |
| }, |
| { |
| name: "Polyspilota aeruginosa", |
| commonName: "Mante cuivrée", |
| taxonKey: "1405481", |
| family: "Mantidae", |
| region: "Afrique subsaharienne", |
| hints: [ |
| "Famille : Mantidae", |
| "Distribution : Afrique subsaharienne", |
| "Coloration vert-cuivré caractéristique, espèce robuste" |
| ] |
| }, |
| { |
| name: "Argema mimosae", |
| commonName: "Papillon lune africain", |
| taxonKey: "1867009", |
| family: "Saturniidae", |
| region: "Afrique australe", |
| hints: [ |
| "Famille : Saturniidae", |
| "Distribution : Afrique australe", |
| "Magnifique papillon de nuit avec de longues queues sur les ailes" |
| ] |
| }, |
| { |
| name: "Acontista mexicana", |
| commonName: "Mante mexicaine", |
| taxonKey: "1404204", |
| family: "Acontistidae", |
| region: "Amérique centrale", |
| hints: [ |
| "Famille : Acontistidae", |
| "Distribution : Amérique centrale, notamment Mexique", |
| "Petite mante discrète vivant dans les zones arides mexicaines" |
| ] |
| }, |
| { |
| name: "Prohierodula flavipennis", |
| commonName: "Mante à ailes jaunes", |
| taxonKey: "1406032", |
| family: "Mantidae", |
| region: "Asie du Sud-Est", |
| hints: [ |
| "Famille : Mantidae", |
| "Distribution : Asie du Sud-Est", |
| "Ailes postérieures jaune vif, espèce de taille moyenne" |
| ] |
| }, |
| { |
| name: "Armadillidium werneri", |
| commonName: "Cloporte de Werner", |
| taxonKey: "2203862", |
| family: "Armadillidiidae", |
| region: "Europe", |
| hints: [ |
| "Famille : Armadillidiidae", |
| "Distribution : Europe", |
| "Petit crustacé terrestre capable de se rouler en boule" |
| ] |
| }, |
| { |
| name: "Carausius morosus", |
| commonName: "Phasme bâton indien", |
| taxonKey: "1411953", |
| family: "Lonchodidae", |
| region: "Inde", |
| hints: [ |
| "Famille : Lonchodidae", |
| "Distribution : Inde", |
| "Phasme très populaire en élevage, reproduction par parthénogenèse" |
| ] |
| }, |
| { |
| name: "Goliathus goliatus", |
| commonName: "Goliath géant", |
| taxonKey: "1076779", |
| family: "Scarabaeidae", |
| region: "Afrique équatoriale", |
| hints: [ |
| "Famille : Scarabaeidae", |
| "Distribution : Afrique équatoriale", |
| "Un des plus grands coléoptères au monde, motifs noir et blanc spectaculaires" |
| ] |
| }, |
| { |
| name: "Eudicella morgani", |
| commonName: "Cétoine de Morgan", |
| taxonKey: "1082835", |
| family: "Scarabaeidae", |
| region: "Afrique centrale", |
| hints: [ |
| "Famille : Scarabaeidae", |
| "Distribution : Afrique centrale", |
| "Magnifique cétoine aux couleurs métalliques et motifs contrastés" |
| ] |
| }, |
| { |
| name: "Mecynorhina harrisii", |
| commonName: "Cétoine de Harris", |
| taxonKey: "1081449", |
| family: "Scarabaeidae", |
| region: "Afrique centrale", |
| hints: [ |
| "Famille : Scarabaeidae", |
| "Distribution : Afrique centrale", |
| "Grande cétoine colorée avec motifs jaunes et noirs" |
| ] |
| }, |
| { |
| name: "Idolomantis diabolica", |
| commonName: "Mante diable géante", |
| taxonKey: "1406692", |
| family: "Empusidae", |
| region: "Afrique de l'Est", |
| hints: [ |
| "Famille : Empusidae", |
| "Distribution : Afrique de l'Est", |
| "Mante spectaculaire avec crête et coloration d'avertissement" |
| ] |
| }, |
| { |
| name: "Asbolus verrucosus", |
| commonName: "Coléoptère cuirassé du désert", |
| taxonKey: "9670677", |
| family: "Tenebrionidae", |
| region: "Sud-ouest des États-Unis", |
| hints: [ |
| "Famille : Tenebrionidae", |
| "Distribution : Sud-ouest des États-Unis, déserts", |
| "Coléoptère du désert avec surface rugueuse et cireuse" |
| ] |
| }, |
| { |
| name: "Atta mexicana", |
| commonName: "Fourmi champignonniste mexicaine", |
| taxonKey: "5035745", |
| family: "Formicidae", |
| region: "Amérique centrale", |
| hints: [ |
| "Famille : Formicidae", |
| "Distribution : Amérique centrale, notamment Mexique", |
| "Fourmi coupeuse de feuilles qui cultive des champignons" |
| ] |
| }, |
| { |
| name: "Acromyrmex octospinosus", |
| commonName: "Fourmi champignonniste épineuse", |
| taxonKey: "1320686", |
| family: "Formicidae", |
| region: "Amérique centrale et du Sud", |
| hints: [ |
| "Famille : Formicidae", |
| "Distribution : Amérique centrale et du Sud", |
| "Fourmi coupeuse de feuilles avec épines sur le thorax" |
| ] |
| }, |
| { |
| name: "Macrophasma lyratus", |
| commonName: "Phasme lyre", |
| taxonKey: "1412429", |
| family: "Phasmatidae", |
| region: "Asie", |
| hints: [ |
| "Famille : Phasmatidae", |
| "Distribution : Asie", |
| "Phasme de taille moyenne avec forme de corps distinctive" |
| ] |
| }, |
| { |
| name: "Myrmecocystus", |
| commonName: "Fourmi pot de miel", |
| taxonKey: "1316742", |
| family: "Formicidae", |
| region: "Amérique du Nord", |
| hints: [ |
| "Famille : Formicidae", |
| "Distribution : Régions arides d'Amérique du Nord", |
| "Fourmis avec individus spécialisés servant de réservoirs de nourriture" |
| ] |
| }, |
| { |
| name: "Sphodromantis viridis", |
| commonName: "Mante africaine géante", |
| taxonKey: "1404864", |
| family: "Mantidae", |
| region: "Afrique", |
| hints: [ |
| "Famille : Mantidae", |
| "Distribution : Afrique subsaharienne", |
| "Grande mante verte robuste, très populaire en terrariophilie" |
| ] |
| } |
| ]; |
| |
| |
| document.getElementById('total-species').textContent = speciesData.length; |
| |
| |
| let currentSpecies = null; |
| let score = 0; |
| let hintsRevealed = 0; |
| let answered = false; |
| let map = null; |
| let tileLayer = null; |
| let baseLayer = null; |
| let selectedIndex = -1; |
| let filteredSpecies = []; |
| let currentMapType = 'standard'; |
| |
| |
| const mapLayers = { |
| standard: { |
| url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', |
| attribution: '© OpenStreetMap contributors' |
| }, |
| dark: { |
| url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', |
| attribution: '© OpenStreetMap contributors, © CARTO' |
| }, |
| satellite: { |
| url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', |
| attribution: 'Tiles © Esri' |
| } |
| }; |
| |
| |
| function initMap() { |
| map = L.map('map').setView([20, 0], 2); |
| |
| baseLayer = L.tileLayer(mapLayers.standard.url, { |
| attribution: mapLayers.standard.attribution, |
| maxZoom: 18 |
| }).addTo(map); |
| } |
| |
| |
| function changeMapType(type) { |
| if (baseLayer) { |
| map.removeLayer(baseLayer); |
| } |
| |
| currentMapType = type; |
| const config = mapLayers[type]; |
| |
| baseLayer = L.tileLayer(config.url, { |
| attribution: config.attribution, |
| maxZoom: 18 |
| }).addTo(map); |
| |
| |
| document.querySelectorAll('.map-type-btn').forEach(btn => { |
| btn.classList.toggle('active', btn.dataset.map === type); |
| }); |
| } |
| |
| |
| document.querySelectorAll('.map-type-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| changeMapType(btn.dataset.map); |
| }); |
| }); |
| |
| |
| function loadSpeciesOnMap(taxonKey) { |
| if (tileLayer) { |
| map.removeLayer(tileLayer); |
| } |
| |
| tileLayer = L.tileLayer( |
| `https://api.gbif.org/v2/map/occurrence/density/{z}/{x}/{y}@1x.png?taxonKey=${taxonKey}&bin=hex&hexPerTile=100&style=red.marker`, |
| { |
| attribution: '© GBIF', |
| maxZoom: 18 |
| } |
| ); |
| |
| tileLayer.addTo(map); |
| } |
| |
| |
| const input = document.getElementById('species-input'); |
| const dropdown = document.getElementById('autocomplete-dropdown'); |
| |
| function filterSpecies(query) { |
| if (!query) return []; |
| const lowerQuery = query.toLowerCase(); |
| return speciesData.filter(species => |
| species.name.toLowerCase().includes(lowerQuery) || |
| species.commonName.toLowerCase().includes(lowerQuery) |
| ).slice(0, 10); |
| } |
| |
| function showAutocomplete(species) { |
| if (species.length === 0) { |
| dropdown.style.display = 'none'; |
| return; |
| } |
| |
| filteredSpecies = species; |
| dropdown.innerHTML = species.map((s, index) => ` |
| <div class="autocomplete-item" data-index="${index}"> |
| <strong>${s.commonName}</strong> |
| <em>${s.name}</em> |
| </div> |
| `).join(''); |
| |
| dropdown.style.display = 'block'; |
| selectedIndex = -1; |
| } |
| |
| input.addEventListener('input', (e) => { |
| const filtered = filterSpecies(e.target.value); |
| showAutocomplete(filtered); |
| }); |
| |
| input.addEventListener('keydown', (e) => { |
| const items = dropdown.querySelectorAll('.autocomplete-item'); |
| |
| if (e.key === 'ArrowDown') { |
| e.preventDefault(); |
| selectedIndex = Math.min(selectedIndex + 1, items.length - 1); |
| updateSelection(items); |
| } else if (e.key === 'ArrowUp') { |
| e.preventDefault(); |
| selectedIndex = Math.max(selectedIndex - 1, 0); |
| updateSelection(items); |
| } else if (e.key === 'Enter') { |
| e.preventDefault(); |
| if (selectedIndex >= 0 && selectedIndex < filteredSpecies.length) { |
| selectSpecies(filteredSpecies[selectedIndex]); |
| } else { |
| submitAnswer(); |
| } |
| } |
| }); |
| |
| function updateSelection(items) { |
| items.forEach((item, index) => { |
| item.classList.toggle('selected', index === selectedIndex); |
| }); |
| if (selectedIndex >= 0) { |
| items[selectedIndex].scrollIntoView({ block: 'nearest' }); |
| } |
| } |
| |
| dropdown.addEventListener('click', (e) => { |
| const item = e.target.closest('.autocomplete-item'); |
| if (item) { |
| const index = parseInt(item.dataset.index); |
| selectSpecies(filteredSpecies[index]); |
| } |
| }); |
| |
| function selectSpecies(species) { |
| input.value = `${species.commonName} (${species.name})`; |
| dropdown.style.display = 'none'; |
| } |
| |
| document.addEventListener('click', (e) => { |
| if (!input.contains(e.target) && !dropdown.contains(e.target)) { |
| dropdown.style.display = 'none'; |
| } |
| }); |
| |
| |
| function startNewRound() { |
| currentSpecies = speciesData[Math.floor(Math.random() * speciesData.length)]; |
| hintsRevealed = 0; |
| answered = false; |
| |
| loadSpeciesOnMap(currentSpecies.taxonKey); |
| |
| document.getElementById('hints-display').innerHTML = ''; |
| document.getElementById('hint-btn').disabled = false; |
| document.getElementById('result-container').innerHTML = ''; |
| document.getElementById('species-input').value = ''; |
| document.getElementById('species-input').disabled = false; |
| document.getElementById('submit-btn').disabled = false; |
| document.getElementById('next-btn').style.display = 'none'; |
| |
| input.focus(); |
| } |
| |
| |
| function submitAnswer() { |
| if (answered || !input.value.trim()) return; |
| |
| answered = true; |
| const userInput = input.value.toLowerCase(); |
| const isCorrect = userInput.includes(currentSpecies.name.toLowerCase()) || |
| userInput.includes(currentSpecies.commonName.toLowerCase()); |
| |
| document.getElementById('species-input').disabled = true; |
| document.getElementById('submit-btn').disabled = true; |
| |
| if (isCorrect) { |
| const points = Math.max(3 - hintsRevealed, 1); |
| score += points; |
| document.getElementById('score').textContent = score; |
| |
| document.getElementById('result-container').innerHTML = ` |
| <div class="result-message correct"> |
| ✓ Correct ! C'était bien ${currentSpecies.commonName}<br> |
| <em>${currentSpecies.name}</em><br> |
| +${points} point${points > 1 ? 's' : ''} |
| </div> |
| `; |
| } else { |
| document.getElementById('result-container').innerHTML = ` |
| <div class="result-message incorrect"> |
| ✗ Incorrect ! C'était ${currentSpecies.commonName}<br> |
| <em>${currentSpecies.name}</em> |
| </div> |
| `; |
| } |
| |
| document.getElementById('next-btn').style.display = 'block'; |
| document.getElementById('hint-btn').disabled = true; |
| } |
| |
| document.getElementById('submit-btn').addEventListener('click', submitAnswer); |
| |
| |
| document.getElementById('hint-btn').addEventListener('click', () => { |
| if (hintsRevealed >= currentSpecies.hints.length || answered) return; |
| |
| const hint = currentSpecies.hints[hintsRevealed]; |
| const hintElement = document.createElement('div'); |
| hintElement.className = 'hint-item'; |
| hintElement.innerHTML = ` |
| <div class="hint-label">Indice ${hintsRevealed + 1} :</div> |
| ${hint} |
| `; |
| |
| document.getElementById('hints-display').appendChild(hintElement); |
| hintsRevealed++; |
| |
| if (hintsRevealed >= currentSpecies.hints.length) { |
| document.getElementById('hint-btn').disabled = true; |
| document.getElementById('hint-btn').textContent = 'Tous les indices révélés'; |
| } |
| }); |
| |
| |
| document.getElementById('next-btn').addEventListener('click', startNewRound); |
| |
| |
| initMap(); |
| startNewRound(); |
| </script> |
| </body> |
| </html> |