Download index.html from Enoder/Insecte: direct link, hf CLI and curl.
- Browser
- Download file 35.1 kB
-
https://huggingface.co/spaces/Enoder/Insecte/resolve/main/index.html
- Command line
-
hf download hf://spaces/Enoder/Insecte/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Enoder/Insecte/resolve/main/index.html
35.1 kB
| <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) ; | |
| font-size: 0.7rem ; | |
| } | |
| </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> | |
| // Complete species database with verified GBIF taxonKeys | |
| 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" | |
| ] | |
| } | |
| ]; | |
| // Update total species count | |
| document.getElementById('total-species').textContent = speciesData.length; | |
| // Game state | |
| 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'; | |
| // Map configurations | |
| 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' | |
| } | |
| }; | |
| // Initialize map | |
| function initMap() { | |
| map = L.map('map').setView([20, 0], 2); | |
| baseLayer = L.tileLayer(mapLayers.standard.url, { | |
| attribution: mapLayers.standard.attribution, | |
| maxZoom: 18 | |
| }).addTo(map); | |
| } | |
| // Change map type | |
| 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); | |
| // Update active button | |
| document.querySelectorAll('.map-type-btn').forEach(btn => { | |
| btn.classList.toggle('active', btn.dataset.map === type); | |
| }); | |
| } | |
| // Add event listeners for map type buttons | |
| document.querySelectorAll('.map-type-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| changeMapType(btn.dataset.map); | |
| }); | |
| }); | |
| // Load species occurrences on 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); | |
| } | |
| // Autocomplete functionality | |
| 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); // Limit to 10 results | |
| } | |
| 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'; | |
| } | |
| }); | |
| // Start new round | |
| 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(); | |
| } | |
| // Submit answer | |
| 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); | |
| // Handle hint request | |
| 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'; | |
| } | |
| }); | |
| // Handle next round | |
| document.getElementById('next-btn').addEventListener('click', startNewRound); | |
| // Initialize game | |
| initMap(); | |
| startNewRound(); | |
| </script> | |
| </body> | |
| </html> |