Enoder commited on
Commit
ec63190
·
verified ·
1 Parent(s): c12162b

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +484 -150
index.html CHANGED
@@ -30,7 +30,6 @@
30
  min-height: 100vh;
31
  }
32
 
33
- /* Texture overlay */
34
  body::before {
35
  content: '';
36
  position: fixed;
@@ -79,7 +78,7 @@
79
 
80
  .game-container {
81
  display: grid;
82
- grid-template-columns: 1fr 380px;
83
  gap: 2rem;
84
  animation: fadeInUp 1s ease 0.2s both;
85
  }
@@ -120,63 +119,139 @@
120
  display: flex;
121
  flex-direction: column;
122
  gap: 1.5rem;
 
 
123
  }
124
 
125
- .species-options {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  display: flex;
127
  flex-direction: column;
128
- gap: 0.75rem;
129
  }
130
 
131
- .species-btn {
132
- background: var(--sepia);
133
- border: 2px solid var(--border);
134
- padding: 1rem 1.25rem;
135
- border-radius: 10px;
136
  font-family: 'Crimson Pro', serif;
137
- font-size: 1.05rem;
138
- color: var(--dark-sepia);
139
- cursor: pointer;
140
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
141
- text-align: left;
 
142
  position: relative;
143
- overflow: hidden;
144
  }
145
 
146
- .species-btn::before {
147
- content: '';
148
- position: absolute;
149
- top: 0;
150
- left: -100%;
151
  width: 100%;
152
- height: 100%;
153
- background: linear-gradient(90deg, transparent, rgba(74, 124, 89, 0.1), transparent);
154
- transition: left 0.5s;
 
 
 
 
 
155
  }
156
 
157
- .species-btn:hover {
158
- background: white;
159
  border-color: var(--accent-green);
160
- transform: translateX(4px);
161
  box-shadow: 0 4px 12px var(--shadow);
162
  }
163
 
164
- .species-btn:hover::before {
165
- left: 100%;
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
 
168
- .species-btn.correct {
169
- background: var(--accent-green);
170
- color: white;
171
- border-color: var(--accent-green);
172
- animation: correctPulse 0.6s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
- .species-btn.incorrect {
176
- background: #c54a4a;
 
 
 
 
 
 
177
  color: white;
178
- border-color: #c54a4a;
179
- animation: shake 0.5s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  }
181
 
182
  .hints-section {
@@ -199,15 +274,11 @@
199
  box-shadow: 0 4px 12px rgba(212, 165, 116, 0.3);
200
  }
201
 
202
- .hint-btn:hover {
203
  transform: translateY(-2px);
204
  box-shadow: 0 6px 20px rgba(212, 165, 116, 0.4);
205
  }
206
 
207
- .hint-btn:active {
208
- transform: translateY(0);
209
- }
210
-
211
  .hint-btn:disabled {
212
  opacity: 0.5;
213
  cursor: not-allowed;
@@ -236,31 +307,6 @@
236
  margin-bottom: 0.25rem;
237
  }
238
 
239
- .score-display {
240
- background: linear-gradient(135deg, #f8f6f0 0%, #ede9dc 100%);
241
- padding: 1.5rem;
242
- border-radius: 10px;
243
- text-align: center;
244
- border: 2px solid var(--border);
245
- }
246
-
247
- .score-label {
248
- font-family: 'Crimson Pro', serif;
249
- font-size: 0.9rem;
250
- color: var(--dark-sepia);
251
- opacity: 0.7;
252
- text-transform: uppercase;
253
- letter-spacing: 0.1em;
254
- margin-bottom: 0.5rem;
255
- }
256
-
257
- .score-value {
258
- font-size: 2.5rem;
259
- font-weight: 700;
260
- color: var(--accent-green);
261
- font-family: 'Crimson Pro', serif;
262
- }
263
-
264
  .next-btn {
265
  background: var(--accent-green);
266
  color: white;
@@ -282,11 +328,6 @@
282
  box-shadow: 0 6px 20px rgba(74, 124, 89, 0.4);
283
  }
284
 
285
- .next-btn:disabled {
286
- opacity: 0.5;
287
- cursor: not-allowed;
288
- }
289
-
290
  .result-message {
291
  padding: 1.5rem;
292
  border-radius: 10px;
@@ -309,7 +350,6 @@
309
  border: 2px solid #c54a4a;
310
  }
311
 
312
- /* Animations */
313
  @keyframes fadeInDown {
314
  from {
315
  opacity: 0;
@@ -348,18 +388,6 @@
348
  to { opacity: 1; }
349
  }
350
 
351
- @keyframes correctPulse {
352
- 0%, 100% { transform: scale(1); }
353
- 50% { transform: scale(1.05); }
354
- }
355
-
356
- @keyframes shake {
357
- 0%, 100% { transform: translateX(0); }
358
- 25% { transform: translateX(-10px); }
359
- 75% { transform: translateX(10px); }
360
- }
361
-
362
- /* Responsive */
363
  @media (max-width: 1024px) {
364
  .game-container {
365
  grid-template-columns: 1fr;
@@ -374,7 +402,6 @@
374
  }
375
  }
376
 
377
- /* Leaflet attribution styling */
378
  .leaflet-control-attribution {
379
  background: rgba(255, 255, 255, 0.8) !important;
380
  font-size: 0.7rem !important;
@@ -404,17 +431,27 @@
404
 
405
  <div id="result-container"></div>
406
 
407
- <div class="species-options" id="species-options">
408
- <!-- Options will be generated here -->
 
 
 
 
 
 
 
 
 
 
 
 
409
  </div>
410
 
411
  <div class="hints-section">
412
  <button class="hint-btn" id="hint-btn">
413
  💡 Demander un indice
414
  </button>
415
- <div class="hints-display" id="hints-display">
416
- <!-- Hints will appear here -->
417
- </div>
418
  </div>
419
 
420
  <button class="next-btn" id="next-btn" style="display: none;">
@@ -426,12 +463,14 @@
426
 
427
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
428
  <script>
429
- // Species data with taxonKeys and hints
430
  const speciesData = [
 
431
  {
432
  name: "Mantis religiosa",
433
  commonName: "Mante religieuse",
434
  taxonKey: "144094823",
 
435
  hints: [
436
  "Famille : Mantidae",
437
  "C'est l'espèce de mante la plus commune et répandue en Europe",
@@ -442,6 +481,7 @@
442
  name: "Iris oratoria",
443
  commonName: "Mante méditerranéenne",
444
  taxonKey: "1404300",
 
445
  hints: [
446
  "Famille : Tarachodidae",
447
  "Espèce méditerranéenne qui s'étend vers le nord",
@@ -452,6 +492,7 @@
452
  name: "Hierodula patellifera",
453
  commonName: "Mante asiatique géante",
454
  taxonKey: "1405951",
 
455
  hints: [
456
  "Famille : Mantidae",
457
  "Une des plus grandes espèces, originaire d'Asie du Sud-Est",
@@ -462,6 +503,7 @@
462
  name: "Sphodromantis viridis",
463
  commonName: "Mante africaine",
464
  taxonKey: "7204692",
 
465
  hints: [
466
  "Famille : Mantidae",
467
  "Espèce africaine de couleur verte caractéristique",
@@ -472,6 +514,7 @@
472
  name: "Blepharopsis mendica",
473
  commonName: "Mante fleur égyptienne",
474
  taxonKey: "1406697",
 
475
  hints: [
476
  "Famille : Empusidae",
477
  "Originaire d'Afrique du Nord et du Moyen-Orient",
@@ -482,11 +525,255 @@
482
  name: "Idolomantis diabolica",
483
  commonName: "Mante fleur du diable",
484
  taxonKey: "1406692",
 
485
  hints: [
486
  "Famille : Empusidae",
487
  "Espèce africaine considérée comme la plus spectaculaire au monde",
488
  "Appelée 'du diable' pour son apparence impressionnante et ses cornes"
489
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  }
491
  ];
492
 
@@ -497,12 +784,13 @@
497
  let answered = false;
498
  let map = null;
499
  let tileLayer = null;
 
 
500
 
501
  // Initialize map
502
  function initMap() {
503
  map = L.map('map').setView([20, 0], 2);
504
 
505
- // Base map layer
506
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
507
  attribution: '© OpenStreetMap contributors',
508
  maxZoom: 18
@@ -511,12 +799,10 @@
511
 
512
  // Load species occurrences on map
513
  function loadSpeciesOnMap(taxonKey) {
514
- // Remove previous tile layer if exists
515
  if (tileLayer) {
516
  map.removeLayer(tileLayer);
517
  }
518
 
519
- // Add GBIF occurrence tile layer
520
  tileLayer = L.tileLayer(
521
  `https://api.gbif.org/v2/map/occurrence/density/{z}/{x}/{y}@1x.png?taxonKey=${taxonKey}&bin=hex&hexPerTile=100&style=classic.point`,
522
  {
@@ -528,78 +814,122 @@
528
  tileLayer.addTo(map);
529
  }
530
 
531
- // Shuffle array
532
- function shuffle(array) {
533
- const shuffled = [...array];
534
- for (let i = shuffled.length - 1; i > 0; i--) {
535
- const j = Math.floor(Math.random() * (i + 1));
536
- [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
 
 
 
 
 
 
 
 
 
 
 
537
  }
538
- return shuffled;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  }
540
 
 
 
 
 
 
 
541
  // Start new round
542
  function startNewRound() {
543
- // Pick random species
544
  currentSpecies = speciesData[Math.floor(Math.random() * speciesData.length)];
545
  hintsRevealed = 0;
546
  answered = false;
547
 
548
- // Load map
549
  loadSpeciesOnMap(currentSpecies.taxonKey);
550
 
551
- // Clear hints
552
  document.getElementById('hints-display').innerHTML = '';
553
  document.getElementById('hint-btn').disabled = false;
554
-
555
- // Clear result
556
  document.getElementById('result-container').innerHTML = '';
557
-
558
- // Generate options (correct + 3 random wrong ones)
559
- const options = [currentSpecies];
560
- const otherSpecies = speciesData.filter(s => s !== currentSpecies);
561
- const shuffledOthers = shuffle(otherSpecies);
562
- options.push(...shuffledOthers.slice(0, 3));
563
-
564
- const shuffledOptions = shuffle(options);
565
-
566
- // Render options
567
- const optionsContainer = document.getElementById('species-options');
568
- optionsContainer.innerHTML = shuffledOptions.map(species => `
569
- <button class="species-btn" data-species="${species.name}">
570
- <strong>${species.commonName}</strong>
571
- <br><em>${species.name}</em>
572
- </button>
573
- `).join('');
574
-
575
- // Add event listeners
576
- document.querySelectorAll('.species-btn').forEach(btn => {
577
- btn.addEventListener('click', handleAnswer);
578
- });
579
-
580
- // Hide next button
581
  document.getElementById('next-btn').style.display = 'none';
 
 
582
  }
583
 
584
- // Handle answer
585
- function handleAnswer(e) {
586
- if (answered) return;
587
 
588
  answered = true;
589
- const selectedSpecies = e.currentTarget.dataset.species;
590
- const isCorrect = selectedSpecies === currentSpecies.name;
591
-
592
- // Disable all buttons
593
- document.querySelectorAll('.species-btn').forEach(btn => {
594
- btn.style.pointerEvents = 'none';
595
- if (btn.dataset.species === currentSpecies.name) {
596
- btn.classList.add('correct');
597
- } else if (btn.dataset.species === selectedSpecies && !isCorrect) {
598
- btn.classList.add('incorrect');
599
- }
600
- });
601
 
602
- // Update score
603
  if (isCorrect) {
604
  const points = Math.max(3 - hintsRevealed, 1);
605
  score += points;
@@ -607,22 +937,26 @@
607
 
608
  document.getElementById('result-container').innerHTML = `
609
  <div class="result-message correct">
610
- ✓ Correct ! +${points} point${points > 1 ? 's' : ''}
 
 
611
  </div>
612
  `;
613
  } else {
614
  document.getElementById('result-container').innerHTML = `
615
  <div class="result-message incorrect">
616
- ✗ Incorrect ! C'était ${currentSpecies.commonName}
 
617
  </div>
618
  `;
619
  }
620
 
621
- // Show next button
622
  document.getElementById('next-btn').style.display = 'block';
623
  document.getElementById('hint-btn').disabled = true;
624
  }
625
 
 
 
626
  // Handle hint request
627
  document.getElementById('hint-btn').addEventListener('click', () => {
628
  if (hintsRevealed >= currentSpecies.hints.length || answered) return;
 
30
  min-height: 100vh;
31
  }
32
 
 
33
  body::before {
34
  content: '';
35
  position: fixed;
 
78
 
79
  .game-container {
80
  display: grid;
81
+ grid-template-columns: 1fr 400px;
82
  gap: 2rem;
83
  animation: fadeInUp 1s ease 0.2s both;
84
  }
 
119
  display: flex;
120
  flex-direction: column;
121
  gap: 1.5rem;
122
+ max-height: 800px;
123
+ overflow-y: auto;
124
  }
125
 
126
+ .score-display {
127
+ background: linear-gradient(135deg, #f8f6f0 0%, #ede9dc 100%);
128
+ padding: 1.5rem;
129
+ border-radius: 10px;
130
+ text-align: center;
131
+ border: 2px solid var(--border);
132
+ position: sticky;
133
+ top: 0;
134
+ z-index: 10;
135
+ }
136
+
137
+ .score-label {
138
+ font-family: 'Crimson Pro', serif;
139
+ font-size: 0.9rem;
140
+ color: var(--dark-sepia);
141
+ opacity: 0.7;
142
+ text-transform: uppercase;
143
+ letter-spacing: 0.1em;
144
+ margin-bottom: 0.5rem;
145
+ }
146
+
147
+ .score-value {
148
+ font-size: 2.5rem;
149
+ font-weight: 700;
150
+ color: var(--accent-green);
151
+ font-family: 'Crimson Pro', serif;
152
+ }
153
+
154
+ .answer-input-section {
155
  display: flex;
156
  flex-direction: column;
157
+ gap: 1rem;
158
  }
159
 
160
+ .answer-input-section label {
 
 
 
 
161
  font-family: 'Crimson Pro', serif;
162
+ font-size: 1.1rem;
163
+ font-weight: 600;
164
+ color: var(--accent-green);
165
+ }
166
+
167
+ .input-wrapper {
168
  position: relative;
 
169
  }
170
 
171
+ #species-input {
 
 
 
 
172
  width: 100%;
173
+ padding: 1rem;
174
+ border: 2px solid var(--border);
175
+ border-radius: 10px;
176
+ font-family: 'Crimson Pro', serif;
177
+ font-size: 1rem;
178
+ background: var(--sepia);
179
+ color: var(--dark-sepia);
180
+ transition: all 0.3s ease;
181
  }
182
 
183
+ #species-input:focus {
184
+ outline: none;
185
  border-color: var(--accent-green);
186
+ background: white;
187
  box-shadow: 0 4px 12px var(--shadow);
188
  }
189
 
190
+ .autocomplete-dropdown {
191
+ position: absolute;
192
+ top: 100%;
193
+ left: 0;
194
+ right: 0;
195
+ background: white;
196
+ border: 2px solid var(--accent-green);
197
+ border-top: none;
198
+ border-radius: 0 0 10px 10px;
199
+ max-height: 300px;
200
+ overflow-y: auto;
201
+ z-index: 1000;
202
+ box-shadow: 0 8px 16px var(--shadow);
203
  }
204
 
205
+ .autocomplete-item {
206
+ padding: 0.75rem 1rem;
207
+ cursor: pointer;
208
+ transition: background 0.2s ease;
209
+ border-bottom: 1px solid var(--border);
210
+ }
211
+
212
+ .autocomplete-item:last-child {
213
+ border-bottom: none;
214
+ }
215
+
216
+ .autocomplete-item:hover,
217
+ .autocomplete-item.selected {
218
+ background: var(--sepia);
219
+ }
220
+
221
+ .autocomplete-item strong {
222
+ color: var(--accent-green);
223
+ display: block;
224
+ margin-bottom: 0.25rem;
225
  }
226
 
227
+ .autocomplete-item em {
228
+ color: var(--dark-sepia);
229
+ opacity: 0.7;
230
+ font-size: 0.9rem;
231
+ }
232
+
233
+ .submit-btn {
234
+ background: linear-gradient(135deg, var(--accent-green) 0%, #5a8c69 100%);
235
  color: white;
236
+ border: none;
237
+ padding: 1rem 1.5rem;
238
+ border-radius: 10px;
239
+ font-family: 'Crimson Pro', serif;
240
+ font-size: 1.1rem;
241
+ font-weight: 600;
242
+ cursor: pointer;
243
+ transition: all 0.3s ease;
244
+ box-shadow: 0 4px 12px rgba(74, 124, 89, 0.3);
245
+ }
246
+
247
+ .submit-btn:hover:not(:disabled) {
248
+ transform: translateY(-2px);
249
+ box-shadow: 0 6px 20px rgba(74, 124, 89, 0.4);
250
+ }
251
+
252
+ .submit-btn:disabled {
253
+ opacity: 0.5;
254
+ cursor: not-allowed;
255
  }
256
 
257
  .hints-section {
 
274
  box-shadow: 0 4px 12px rgba(212, 165, 116, 0.3);
275
  }
276
 
277
+ .hint-btn:hover:not(:disabled) {
278
  transform: translateY(-2px);
279
  box-shadow: 0 6px 20px rgba(212, 165, 116, 0.4);
280
  }
281
 
 
 
 
 
282
  .hint-btn:disabled {
283
  opacity: 0.5;
284
  cursor: not-allowed;
 
307
  margin-bottom: 0.25rem;
308
  }
309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  .next-btn {
311
  background: var(--accent-green);
312
  color: white;
 
328
  box-shadow: 0 6px 20px rgba(74, 124, 89, 0.4);
329
  }
330
 
 
 
 
 
 
331
  .result-message {
332
  padding: 1.5rem;
333
  border-radius: 10px;
 
350
  border: 2px solid #c54a4a;
351
  }
352
 
 
353
  @keyframes fadeInDown {
354
  from {
355
  opacity: 0;
 
388
  to { opacity: 1; }
389
  }
390
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  @media (max-width: 1024px) {
392
  .game-container {
393
  grid-template-columns: 1fr;
 
402
  }
403
  }
404
 
 
405
  .leaflet-control-attribution {
406
  background: rgba(255, 255, 255, 0.8) !important;
407
  font-size: 0.7rem !important;
 
431
 
432
  <div id="result-container"></div>
433
 
434
+ <div class="answer-input-section">
435
+ <label for="species-input">Quelle est cette espèce ?</label>
436
+ <div class="input-wrapper">
437
+ <input
438
+ type="text"
439
+ id="species-input"
440
+ placeholder="Tapez le nom de l'espèce..."
441
+ autocomplete="off"
442
+ />
443
+ <div id="autocomplete-dropdown" class="autocomplete-dropdown" style="display: none;"></div>
444
+ </div>
445
+ <button class="submit-btn" id="submit-btn">
446
+ Valider ma réponse
447
+ </button>
448
  </div>
449
 
450
  <div class="hints-section">
451
  <button class="hint-btn" id="hint-btn">
452
  💡 Demander un indice
453
  </button>
454
+ <div class="hints-display" id="hints-display"></div>
 
 
455
  </div>
456
 
457
  <button class="next-btn" id="next-btn" style="display: none;">
 
463
 
464
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
465
  <script>
466
+ // Extended species database with taxonKeys and hints
467
  const speciesData = [
468
+ // Original species
469
  {
470
  name: "Mantis religiosa",
471
  commonName: "Mante religieuse",
472
  taxonKey: "144094823",
473
+ family: "Mantidae",
474
  hints: [
475
  "Famille : Mantidae",
476
  "C'est l'espèce de mante la plus commune et répandue en Europe",
 
481
  name: "Iris oratoria",
482
  commonName: "Mante méditerranéenne",
483
  taxonKey: "1404300",
484
+ family: "Tarachodidae",
485
  hints: [
486
  "Famille : Tarachodidae",
487
  "Espèce méditerranéenne qui s'étend vers le nord",
 
492
  name: "Hierodula patellifera",
493
  commonName: "Mante asiatique géante",
494
  taxonKey: "1405951",
495
+ family: "Mantidae",
496
  hints: [
497
  "Famille : Mantidae",
498
  "Une des plus grandes espèces, originaire d'Asie du Sud-Est",
 
503
  name: "Sphodromantis viridis",
504
  commonName: "Mante africaine",
505
  taxonKey: "7204692",
506
+ family: "Mantidae",
507
  hints: [
508
  "Famille : Mantidae",
509
  "Espèce africaine de couleur verte caractéristique",
 
514
  name: "Blepharopsis mendica",
515
  commonName: "Mante fleur égyptienne",
516
  taxonKey: "1406697",
517
+ family: "Empusidae",
518
  hints: [
519
  "Famille : Empusidae",
520
  "Originaire d'Afrique du Nord et du Moyen-Orient",
 
525
  name: "Idolomantis diabolica",
526
  commonName: "Mante fleur du diable",
527
  taxonKey: "1406692",
528
+ family: "Empusidae",
529
  hints: [
530
  "Famille : Empusidae",
531
  "Espèce africaine considérée comme la plus spectaculaire au monde",
532
  "Appelée 'du diable' pour son apparence impressionnante et ses cornes"
533
  ]
534
+ },
535
+ // New species additions
536
+ {
537
+ name: "Acontista mexicana",
538
+ commonName: "Mante mexicaine",
539
+ taxonKey: "1404204",
540
+ family: "Acontistidae",
541
+ hints: [
542
+ "Famille : Acontistidae",
543
+ "Espèce d'Amérique centrale, notamment au Mexique",
544
+ "Petite mante discrète vivant dans les zones arides"
545
+ ]
546
+ },
547
+ {
548
+ name: "Empusa pennata",
549
+ commonName: "Empuse commune",
550
+ taxonKey: "1406674",
551
+ family: "Empusidae",
552
+ hints: [
553
+ "Famille : Empusidae",
554
+ "Espèce méditerranéenne avec des antennes plumeuses caractéristiques",
555
+ "Silhouette très élancée avec une crête sur la tête"
556
+ ]
557
+ },
558
+ {
559
+ name: "Tenodera sinensis",
560
+ commonName: "Mante chinoise",
561
+ taxonKey: "1405978",
562
+ family: "Mantidae",
563
+ hints: [
564
+ "Famille : Mantidae",
565
+ "Grande mante originaire d'Asie, introduite en Amérique du Nord",
566
+ "Une des espèces les plus grandes et les plus robustes"
567
+ ]
568
+ },
569
+ {
570
+ name: "Phyllocrania paradoxa",
571
+ commonName: "Mante feuille morte",
572
+ taxonKey: "1406700",
573
+ family: "Hymenopodidae",
574
+ hints: [
575
+ "Famille : Hymenopodidae",
576
+ "Originaire d'Afrique, mimétisme exceptionnel avec une feuille morte",
577
+ "Petite espèce très appréciée en terrariophilie"
578
+ ]
579
+ },
580
+ {
581
+ name: "Hymenopus coronatus",
582
+ commonName: "Mante orchidée",
583
+ taxonKey: "1406703",
584
+ family: "Hymenopodidae",
585
+ hints: [
586
+ "Famille : Hymenopodidae",
587
+ "Espèce d'Asie du Sud-Est, célèbre pour son mimétisme avec les orchidées",
588
+ "Considérée comme une des plus belles mantes du monde"
589
+ ]
590
+ },
591
+ {
592
+ name: "Creobroter gemmatus",
593
+ commonName: "Mante fleur indienne",
594
+ taxonKey: "1406706",
595
+ family: "Hymenopodidae",
596
+ hints: [
597
+ "Famille : Hymenopodidae",
598
+ "Petite mante d'Asie du Sud avec des motifs colorés",
599
+ "Taches oculaires vertes sur les ailes antérieures"
600
+ ]
601
+ },
602
+ {
603
+ name: "Pseudocreobotra wahlbergii",
604
+ commonName: "Mante fleur épineuse",
605
+ taxonKey: "1406705",
606
+ family: "Hymenopodidae",
607
+ hints: [
608
+ "Famille : Hymenopodidae",
609
+ "Espèce africaine avec des motifs en spirale sur les ailes",
610
+ "Abdomen en forme de feuille avec des épines"
611
+ ]
612
+ },
613
+ {
614
+ name: "Deroplatys desiccata",
615
+ commonName: "Mante feuille morte géante",
616
+ taxonKey: "1406699",
617
+ family: "Deroplatyidae",
618
+ hints: [
619
+ "Famille : Deroplatyidae",
620
+ "Grande espèce d'Asie du Sud-Est avec mimétisme de feuille morte",
621
+ "Apparence très plate et rugueuse imitant une feuille desséchée"
622
+ ]
623
+ },
624
+ {
625
+ name: "Stagmomantis carolina",
626
+ commonName: "Mante de Caroline",
627
+ taxonKey: "1405980",
628
+ family: "Mantidae",
629
+ hints: [
630
+ "Famille : Mantidae",
631
+ "Espèce commune aux États-Unis, notamment dans le sud-est",
632
+ "Mante de taille moyenne, très adaptable"
633
+ ]
634
+ },
635
+ {
636
+ name: "Ameles spallanzania",
637
+ commonName: "Amèle de Spallanzani",
638
+ taxonKey: "1404283",
639
+ family: "Amelidae",
640
+ hints: [
641
+ "Famille : Amelidae",
642
+ "Petite mante méditerranéenne",
643
+ "Les femelles sont souvent aptères (sans ailes)"
644
+ ]
645
+ },
646
+ {
647
+ name: "Popa spurca",
648
+ commonName: "Mante écorce africaine",
649
+ taxonKey: "1406694",
650
+ family: "Liturgusidae",
651
+ hints: [
652
+ "Famille : Liturgusidae",
653
+ "Espèce africaine avec excellent mimétisme d'écorce",
654
+ "Apparence plate et rugueuse, vit sur les troncs d'arbres"
655
+ ]
656
+ },
657
+ {
658
+ name: "Sibylla pretiosa",
659
+ commonName: "Mante cryptique",
660
+ taxonKey: "1406695",
661
+ family: "Sibyllidae",
662
+ hints: [
663
+ "Famille : Sibyllidae",
664
+ "Espèce d'Afrique australe au camouflage remarquable",
665
+ "Forme et couleur imitant l'écorce ou les brindilles"
666
+ ]
667
+ },
668
+ {
669
+ name: "Miomantis paykulli",
670
+ commonName: "Mante égyptienne",
671
+ taxonKey: "1405984",
672
+ family: "Mantidae",
673
+ hints: [
674
+ "Famille : Mantidae",
675
+ "Petite mante africaine, introduite dans plusieurs régions",
676
+ "Très adaptable, peut vivre en zones urbaines"
677
+ ]
678
+ },
679
+ {
680
+ name: "Hierodula membranacea",
681
+ commonName: "Mante asiatique géante",
682
+ taxonKey: "1405953",
683
+ family: "Mantidae",
684
+ hints: [
685
+ "Famille : Mantidae",
686
+ "Grande espèce d'Asie, très répandue en terrariophilie",
687
+ "Couleur verte brillante, très vorace"
688
+ ]
689
+ },
690
+ {
691
+ name: "Sphodromantis lineola",
692
+ commonName: "Mante africaine lignée",
693
+ taxonKey: "1405996",
694
+ family: "Mantidae",
695
+ hints: [
696
+ "Famille : Mantidae",
697
+ "Grande mante d'Afrique centrale et occidentale",
698
+ "Bande blanche caractéristique sur les ailes antérieures"
699
+ ]
700
+ },
701
+ {
702
+ name: "Archimantis latistyla",
703
+ commonName: "Mante australienne géante",
704
+ taxonKey: "1406667",
705
+ family: "Mantidae",
706
+ hints: [
707
+ "Famille : Mantidae",
708
+ "Grande espèce endémique d'Australie",
709
+ "Une des plus grandes mantes d'Australie"
710
+ ]
711
+ },
712
+ {
713
+ name: "Metallyticus splendidus",
714
+ commonName: "Mante métallique",
715
+ taxonKey: "1406714",
716
+ family: "Metallyticidae",
717
+ hints: [
718
+ "Famille : Metallyticidae",
719
+ "Petite mante d'Asie du Sud-Est avec reflets métalliques",
720
+ "Aspect iridescent bleu-vert unique parmi les mantes"
721
+ ]
722
+ },
723
+ {
724
+ name: "Theopropus elegans",
725
+ commonName: "Mante orchidée élégante",
726
+ taxonKey: "1406707",
727
+ family: "Hymenopodidae",
728
+ hints: [
729
+ "Famille : Hymenopodidae",
730
+ "Espèce d'Asie du Sud-Est ressemblant aux fleurs",
731
+ "Lobes abdominaux en forme de pétales"
732
+ ]
733
+ },
734
+ {
735
+ name: "Rivetina baetica",
736
+ commonName: "Mante ibérique",
737
+ taxonKey: "1404296",
738
+ family: "Rivetinidae",
739
+ hints: [
740
+ "Famille : Rivetinidae",
741
+ "Petite mante de la péninsule ibérique et d'Afrique du Nord",
742
+ "Espèce discrète vivant au sol dans la végétation basse"
743
+ ]
744
+ },
745
+ {
746
+ name: "Empusa fasciata",
747
+ commonName: "Empuse fascié",
748
+ taxonKey: "1406675",
749
+ family: "Empusidae",
750
+ hints: [
751
+ "Famille : Empusidae",
752
+ "Espèce méditerranéenne proche d'Empusa pennata",
753
+ "Antennes plumeuses et corps très élancé"
754
+ ]
755
+ },
756
+ {
757
+ name: "Orthodera ministralis",
758
+ commonName: "Mante australienne jardinière",
759
+ taxonKey: "1406668",
760
+ family: "Mantidae",
761
+ hints: [
762
+ "Famille : Mantidae",
763
+ "Espèce endémique d'Australie",
764
+ "Mante de taille moyenne, verte ou brune"
765
+ ]
766
+ },
767
+ {
768
+ name: "Brunneria borealis",
769
+ commonName: "Mante-bâton",
770
+ taxonKey: "1404272",
771
+ family: "Mantidae",
772
+ hints: [
773
+ "Famille : Mantidae",
774
+ "Espèce d'Amérique du Nord, très élancée",
775
+ "Population entièrement femelle (parthénogenèse)"
776
+ ]
777
  }
778
  ];
779
 
 
784
  let answered = false;
785
  let map = null;
786
  let tileLayer = null;
787
+ let selectedIndex = -1;
788
+ let filteredSpecies = [];
789
 
790
  // Initialize map
791
  function initMap() {
792
  map = L.map('map').setView([20, 0], 2);
793
 
 
794
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
795
  attribution: '© OpenStreetMap contributors',
796
  maxZoom: 18
 
799
 
800
  // Load species occurrences on map
801
  function loadSpeciesOnMap(taxonKey) {
 
802
  if (tileLayer) {
803
  map.removeLayer(tileLayer);
804
  }
805
 
 
806
  tileLayer = L.tileLayer(
807
  `https://api.gbif.org/v2/map/occurrence/density/{z}/{x}/{y}@1x.png?taxonKey=${taxonKey}&bin=hex&hexPerTile=100&style=classic.point`,
808
  {
 
814
  tileLayer.addTo(map);
815
  }
816
 
817
+ // Autocomplete functionality
818
+ const input = document.getElementById('species-input');
819
+ const dropdown = document.getElementById('autocomplete-dropdown');
820
+
821
+ function filterSpecies(query) {
822
+ if (!query) return [];
823
+ const lowerQuery = query.toLowerCase();
824
+ return speciesData.filter(species =>
825
+ species.name.toLowerCase().includes(lowerQuery) ||
826
+ species.commonName.toLowerCase().includes(lowerQuery)
827
+ );
828
+ }
829
+
830
+ function showAutocomplete(species) {
831
+ if (species.length === 0) {
832
+ dropdown.style.display = 'none';
833
+ return;
834
  }
835
+
836
+ filteredSpecies = species;
837
+ dropdown.innerHTML = species.map((s, index) => `
838
+ <div class="autocomplete-item" data-index="${index}">
839
+ <strong>${s.commonName}</strong>
840
+ <em>${s.name}</em>
841
+ </div>
842
+ `).join('');
843
+
844
+ dropdown.style.display = 'block';
845
+ selectedIndex = -1;
846
+ }
847
+
848
+ input.addEventListener('input', (e) => {
849
+ const filtered = filterSpecies(e.target.value);
850
+ showAutocomplete(filtered);
851
+ });
852
+
853
+ input.addEventListener('keydown', (e) => {
854
+ const items = dropdown.querySelectorAll('.autocomplete-item');
855
+
856
+ if (e.key === 'ArrowDown') {
857
+ e.preventDefault();
858
+ selectedIndex = Math.min(selectedIndex + 1, items.length - 1);
859
+ updateSelection(items);
860
+ } else if (e.key === 'ArrowUp') {
861
+ e.preventDefault();
862
+ selectedIndex = Math.max(selectedIndex - 1, 0);
863
+ updateSelection(items);
864
+ } else if (e.key === 'Enter') {
865
+ e.preventDefault();
866
+ if (selectedIndex >= 0 && selectedIndex < filteredSpecies.length) {
867
+ selectSpecies(filteredSpecies[selectedIndex]);
868
+ } else {
869
+ submitAnswer();
870
+ }
871
+ }
872
+ });
873
+
874
+ function updateSelection(items) {
875
+ items.forEach((item, index) => {
876
+ item.classList.toggle('selected', index === selectedIndex);
877
+ });
878
+ if (selectedIndex >= 0) {
879
+ items[selectedIndex].scrollIntoView({ block: 'nearest' });
880
+ }
881
+ }
882
+
883
+ dropdown.addEventListener('click', (e) => {
884
+ const item = e.target.closest('.autocomplete-item');
885
+ if (item) {
886
+ const index = parseInt(item.dataset.index);
887
+ selectSpecies(filteredSpecies[index]);
888
+ }
889
+ });
890
+
891
+ function selectSpecies(species) {
892
+ input.value = `${species.commonName} (${species.name})`;
893
+ dropdown.style.display = 'none';
894
  }
895
 
896
+ document.addEventListener('click', (e) => {
897
+ if (!input.contains(e.target) && !dropdown.contains(e.target)) {
898
+ dropdown.style.display = 'none';
899
+ }
900
+ });
901
+
902
  // Start new round
903
  function startNewRound() {
 
904
  currentSpecies = speciesData[Math.floor(Math.random() * speciesData.length)];
905
  hintsRevealed = 0;
906
  answered = false;
907
 
 
908
  loadSpeciesOnMap(currentSpecies.taxonKey);
909
 
 
910
  document.getElementById('hints-display').innerHTML = '';
911
  document.getElementById('hint-btn').disabled = false;
 
 
912
  document.getElementById('result-container').innerHTML = '';
913
+ document.getElementById('species-input').value = '';
914
+ document.getElementById('species-input').disabled = false;
915
+ document.getElementById('submit-btn').disabled = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  document.getElementById('next-btn').style.display = 'none';
917
+
918
+ input.focus();
919
  }
920
 
921
+ // Submit answer
922
+ function submitAnswer() {
923
+ if (answered || !input.value.trim()) return;
924
 
925
  answered = true;
926
+ const userInput = input.value.toLowerCase();
927
+ const isCorrect = userInput.includes(currentSpecies.name.toLowerCase()) ||
928
+ userInput.includes(currentSpecies.commonName.toLowerCase());
929
+
930
+ document.getElementById('species-input').disabled = true;
931
+ document.getElementById('submit-btn').disabled = true;
 
 
 
 
 
 
932
 
 
933
  if (isCorrect) {
934
  const points = Math.max(3 - hintsRevealed, 1);
935
  score += points;
 
937
 
938
  document.getElementById('result-container').innerHTML = `
939
  <div class="result-message correct">
940
+ ✓ Correct ! C'était bien ${currentSpecies.commonName}<br>
941
+ <em>${currentSpecies.name}</em><br>
942
+ +${points} point${points > 1 ? 's' : ''}
943
  </div>
944
  `;
945
  } else {
946
  document.getElementById('result-container').innerHTML = `
947
  <div class="result-message incorrect">
948
+ ✗ Incorrect ! C'était ${currentSpecies.commonName}<br>
949
+ <em>${currentSpecies.name}</em>
950
  </div>
951
  `;
952
  }
953
 
 
954
  document.getElementById('next-btn').style.display = 'block';
955
  document.getElementById('hint-btn').disabled = true;
956
  }
957
 
958
+ document.getElementById('submit-btn').addEventListener('click', submitAnswer);
959
+
960
  // Handle hint request
961
  document.getElementById('hint-btn').addEventListener('click', () => {
962
  if (hintsRevealed >= currentSpecies.hints.length || answered) return;