First Iteration of Map Question Type
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tsparticles/confetti@3.0.3/tsparticles.confetti.bundle.min.js"></script>
|
||||
<title>qivip</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||
crossorigin=""/>
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<main class="grow">
|
||||
|
||||
@@ -98,9 +98,6 @@
|
||||
Eingabe
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="{% url 'library:new_question' %}?type=multiple_choice&quiz_id={{ quiz.id }}"
|
||||
class=" mt-1 bg-blue-100 text-blue-800 px-4 py-2 rounded-md text-xs lg:text-lg hover:border-blue-600 border-2">
|
||||
Multiple Choice
|
||||
@@ -116,10 +113,15 @@
|
||||
Reihenfolge
|
||||
</a>
|
||||
|
||||
<a href="{% url 'library:new_question' %}?type=guess&quiz_id={{ quiz.id }}"
|
||||
<a href="{% url 'library:new_question' %}?type=guess&quiz_id={{ quiz.id }}"
|
||||
class=" mt-1 bg-pink-100 text-pink-800 px-4 py-2 rounded-md text-xs lg:text-lg hover:border-blue-600 border-2">
|
||||
Schätzen
|
||||
</a>
|
||||
|
||||
<a href="{% url 'library:new_question' %}?type=map&quiz_id={{ quiz.id }}"
|
||||
class=" mt-1 bg-purple-100 text-orange-800 px-4 py-2 rounded-md text-xs lg:text-lg hover:border-orange-600 border-2">
|
||||
Karte
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,8 +142,14 @@
|
||||
<div class="flex-grow">
|
||||
<p class="font-medium">{{ question.data.question }}</p>
|
||||
<div class="mt-1">
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium {% if question.data.type == 'multiple_choice' %}bg-blue-100 text-blue-800 {% elif question.data.type == 'input' %} bg-green-100 text-green-800 {% elif question.data.type == 'true_false' %}bg-purple-100 text-purple-800{% elif question.data.type == 'order' %}bg-yellow-100 text-yellow-800{% else %}bg-pink-100 text-pink-800 {% endif %}">
|
||||
{% if question.data.type == 'multiple_choice' %}Multiple Choice{% elif question.data.type == 'input' %}Eingabe{% elif question.data.type == 'true_false' %}Wahr/Falsch{% elif question.data.type == 'order' %}Reihenfolge{% else %}Schätzen{% endif %}
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium {% if question.data.type == 'multiple_choice' %}bg-blue-100 text-blue-800 {% elif question.data.type == 'input' %} bg-green-100 text-green-800 {% elif question.data.type == 'map' %}bg-orange-100 text-orange-800 {% elif question.data.type == 'true_false' %}bg-purple-100 text-purple-800{% elif question.data.type == 'order' %}bg-yellow-100 text-yellow-800{% else %}bg-pink-100 text-pink-800 {% endif %}">
|
||||
{% if question.data.type == 'multiple_choice' %}Multiple Choice
|
||||
{% elif question.data.type == 'input' %}Eingabe
|
||||
{% elif question.data.type == 'map' %}Karte
|
||||
{% elif question.data.type == 'true_false' %}Wahr/Falsch
|
||||
{% elif question.data.type == 'order' %}Reihenfolge
|
||||
{% else %}Schätzen
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
@@ -173,8 +181,46 @@
|
||||
<img src="{{ question.image.image.url }}" alt="Bild der Frage" class="w-[200px] rounded-lg shadow-md border-blue-600 border-2 rounded-xl shadow-md object-contain mt-4 md:mt-0" />
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% elif question.data.type == 'map' and detail %}
|
||||
<div class="mt-3">
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300">
|
||||
<span class="font-medium">Zielort:</span>
|
||||
<span id="target-location-{{ forloop.counter }}">
|
||||
{% if question.data.target_location %}
|
||||
{{ question.data.target_location }}
|
||||
{% else %}
|
||||
Kein Zielort festgelegt
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% if question.data.target_location %}
|
||||
<script>
|
||||
// Format the coordinates to 4 decimal places
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const targetEl = document.getElementById('target-location-{{ forloop.counter }}');
|
||||
if (targetEl) {
|
||||
const coords = targetEl.textContent.trim().split(',');
|
||||
if (coords.length === 2) {
|
||||
const lat = parseFloat(coords[0]).toFixed(4);
|
||||
const lng = parseFloat(coords[1]).toFixed(4);
|
||||
targetEl.textContent = `${lat}, ${lng}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if question.data.tolerance_radius %}
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300 mt-1">
|
||||
<span class="font-medium">Toleranzradius:</span> {{ question.data.tolerance_radius }} Meter
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if question.image.image.url %}
|
||||
<img src="{{ question.image.image.url }}" alt="Bild der Frage" class="w-[200px] rounded-lg shadow-md border-blue-600 border-2 rounded-xl shadow-md object-contain mt-4" />
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elif question.data.type == 'order' %}
|
||||
<div class="w-full gap-4 md:flex justify-between ">
|
||||
<div class="w-full gap-4 md:flex justify-between ">
|
||||
<ul class="space-y-1 ">
|
||||
{% for option in question.data.options|dictsort:"order" %}
|
||||
<li class="flex items-center text-sm">
|
||||
@@ -188,7 +234,6 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
{% elif question.data.type == 'input' %}
|
||||
<div class="w-full gap-4 md:flex justify-between ">
|
||||
<ul class="space-y-1 ">
|
||||
|
||||
131
django/templates/library/question/question_map.html
Normal file
131
django/templates/library/question/question_map.html
Normal file
@@ -0,0 +1,131 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>
|
||||
<div id="mapquestion-container">
|
||||
<div>
|
||||
<form class="flex flex-col p-4 space-y-4" enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="relative">
|
||||
<label for="question" class="absolute -top-2 left-3 text-sm bg-white dark:bg-gray-900 px-1 text-blue-600 dark:text-white z-10">Frage</label>
|
||||
<input type="text" id="question" name="question" class="p-4 w-full border-2 border-blue-400 rounded-md dark:text-white" value="{{ question.data.question }}" />
|
||||
</div>
|
||||
{% if image %}
|
||||
<img src="{{ image.image.url }}" style="max-width: 100px;" alt="Bild der Frage">
|
||||
<label for="reset_image" class="dark:text-white">
|
||||
<input type="checkbox" name="reset_ques_image" id="reset_ques_image">
|
||||
Bild zurücksetzen
|
||||
</label>
|
||||
{% endif %}
|
||||
<div class="relative">
|
||||
<label for="question_image" class="absolute -top-2 left-3 text-sm bg-white dark:bg-gray-900 px-1 text-blue-600 dark:text-white z-10">Bild der Frage</label>
|
||||
<input class="p-4 w-full border-2 border-blue-400 rounded-md dark:text-white" type="file" name="question_image" id="question_image" accept="image/*">
|
||||
</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-2">Ziel auswählen</div>
|
||||
<div id="map"></div>
|
||||
<div class="relative">
|
||||
<label for="location" class="absolute -top-2 left-3 text-sm bg-white dark:bg-gray-900 px-1 text-blue-600 dark:text-white z-10">Koordinaten</label>
|
||||
<input type="text" id="location" name="location" class="p-4 w-full border-2 border-blue-400 rounded-md dark:text-white" value="{{ question.data.target_location }}" />
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<label for="time_per_question" class="absolute -top-2 left-3 text-sm bg-white dark:bg-gray-900 px-1 text-blue-600 dark:text-white z-10">Zeit pro Frage</label>
|
||||
<select name="time_per_question" id="time_per_question" class="p-4 w-full border-2 border-blue-400 rounded-md bg-white dark:bg-gray-900 dark:text-gray-300">
|
||||
<option value="10" {% if standard_time_per_question == "10" or time_per_question == "10" %}selected{% endif %}>10 Sekunden</option>
|
||||
<option value="20" {% if standard_time_per_question == "20" or time_per_question == "20" %}selected{% endif %}>20 Sekunden</option>
|
||||
<option value="30" {% if standard_time_per_question == "30" or time_per_question == "30" %}selected{% endif %}>30 Sekunden</option>
|
||||
<option value="45" {% if standard_time_per_question == "45" or time_per_question == "45" %}selected{% endif %}>45 Sekunden</option>
|
||||
<option value="60" {% if standard_time_per_question == "60" or time_per_question == "60" %}selected{% endif %}>1 Minute</option>
|
||||
<option value="120" {% if standard_time_per_question == "120" or time_per_question == "120" %}selected{% endif %}>2 Minuten</option>
|
||||
<option value="300" {% if standard_time_per_question == "300" or time_per_question == "300" %}selected{% endif %}>5 Minuten</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<label for="credits" class="absolute -top-2 left-3 text-sm bg-white dark:bg-gray-900 px-1 text-blue-600 dark:text-white z-10">Credits</label>
|
||||
<textarea name="credits" id="credits" rows="4" class="p-4 w-full border-2 border-blue-400 rounded-md dark:text-white">{{ credits }}</textarea>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<label for="tolerance_radius" class="absolute -top-2 left-3 text-sm bg-white dark:bg-gray-900 px-1 text-blue-600 dark:text-white z-10">Radius (Meter) für volle Punktzahl</label>
|
||||
<input type="number" id="tolerance_radius" name="tolerance_radius" class="p-4 w-full border-2 border-blue-400 rounded-md dark:text-white" min="1" value="{{ question.data.tolerance_radius }}" />
|
||||
</div>
|
||||
<div class="flex justify-between mt-4">
|
||||
<button class="p-2 rounded-md text-white bg-gray-600 hover:bg-gray-700" type="button" onclick="window.history.back()">Zurück</button>
|
||||
<input class="p-2 rounded-md text-white bg-blue-400 hover:bg-blue-500" type="submit" value="Speichern">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let marker = null;
|
||||
let circle = null;
|
||||
const locationInput = document.getElementById('location');
|
||||
const radiusInput = document.getElementById('tolerance_radius');
|
||||
|
||||
const map = L.map('map').setView([51.72, 10.45], 3);
|
||||
|
||||
// Prüfe, ob Dark Mode aktiv ist
|
||||
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
// Wähle das passende Karten-Design
|
||||
const tileUrl = isDarkMode
|
||||
? 'https://{s}.basemaps.cartocdn.com/rastertiles/dark_all/{z}/{x}/{y}.png'
|
||||
: 'https://{s}.basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}.png';
|
||||
|
||||
L.tileLayer(tileUrl, {
|
||||
maxZoom: 19,
|
||||
attribution: '© OpenStreetMap © CARTO'
|
||||
}).addTo(map);
|
||||
|
||||
function updateCircle(latlng) {
|
||||
const radius = parseInt(radiusInput.value) || 100;
|
||||
if (circle) {
|
||||
map.removeLayer(circle);
|
||||
}
|
||||
circle = L.circle(latlng, {
|
||||
color: 'blue',
|
||||
fillColor: '#87CEEB',
|
||||
fillOpacity: 0.2,
|
||||
radius: radius
|
||||
}).addTo(map);
|
||||
}
|
||||
|
||||
function updateInput(latlng) {
|
||||
locationInput.value = `${latlng.lat.toFixed(6)},${latlng.lng.toFixed(6)}`;
|
||||
if (marker) {
|
||||
updateCircle(marker.getLatLng());
|
||||
}
|
||||
}
|
||||
|
||||
function placeOrMoveMarker(latlng) {
|
||||
if (marker) {
|
||||
marker.setLatLng(latlng);
|
||||
} else {
|
||||
marker = L.marker(latlng, { draggable: true }).addTo(map);
|
||||
|
||||
marker.on('dragend', () => {
|
||||
updateInput(marker.getLatLng());
|
||||
});
|
||||
}
|
||||
updateInput(latlng);
|
||||
}
|
||||
|
||||
// Update circle when radius changes
|
||||
radiusInput.addEventListener('input', () => {
|
||||
if (marker) {
|
||||
updateCircle(marker.getLatLng());
|
||||
}
|
||||
});
|
||||
|
||||
// Place marker at existing coordinates if available
|
||||
const existingCoords = '{{ question.data.target_location }}';
|
||||
if (existingCoords) {
|
||||
const [lat, lng] = existingCoords.split(',').map(Number);
|
||||
placeOrMoveMarker({ lat, lng });
|
||||
map.setView([lat, lng], 6); // Zoom in to show the marker
|
||||
}
|
||||
|
||||
map.on('click', e => placeOrMoveMarker(e.latlng));
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -50,8 +50,7 @@
|
||||
|
||||
|
||||
</div>
|
||||
{% if question_data.type == "multiple_choice" or question_data.type == "true_false" %}
|
||||
|
||||
{% if question_data.type == "multiple_choice" or question_data.type == "true_false" %}
|
||||
<div class="grid grid-cols-2 gap-4" id="options-container">
|
||||
{% for option in question_data.options %}
|
||||
<button
|
||||
@@ -162,8 +161,93 @@ class="px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-
|
||||
Abschicken
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% elif question_data.type == "guess" %}
|
||||
|
||||
{% endif %}
|
||||
{% elif question_data.type == "map" and request.session.mode == "learn" %}
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-2">Ziel auswählen</div>
|
||||
<div id="map" class="h-96"></div>
|
||||
<input type="hidden" value="" id="singleanswer" name="singleanswer">
|
||||
<div class="mt-4">
|
||||
<button onclick="submitAnswerMap()" class="bg-blue-400 text-white px-4 py-2 rounded-md hover:bg-blue-500 transition-colors">
|
||||
Abgeben
|
||||
</button>
|
||||
</div>
|
||||
<input type="hidden" id="location" name="location" value="">
|
||||
<script>
|
||||
let marker = null;
|
||||
const locationInput = document.getElementById('location');
|
||||
const map = L.map('map').setView([51.72, 10.45], 3);
|
||||
|
||||
// Prüfe, ob Dark Mode aktiv ist
|
||||
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
// Wähle das passende Karten-Design
|
||||
const tileUrl = isDarkMode
|
||||
? 'https://{s}.basemaps.cartocdn.com/rastertiles/dark_nolabels/{z}/{x}/{y}.png'
|
||||
: 'https://{s}.basemaps.cartocdn.com/rastertiles/light_nolabels/{z}/{x}/{y}.png';
|
||||
|
||||
L.tileLayer(tileUrl, {
|
||||
maxZoom: 19,
|
||||
attribution: '© OpenStreetMap © CARTO'
|
||||
}).addTo(map);
|
||||
|
||||
function updateInput(latlng) {
|
||||
locationInput.value = `${latlng.lat.toFixed(6)},${latlng.lng.toFixed(6)}`;
|
||||
}
|
||||
|
||||
function placeOrMoveMarker(latlng) {
|
||||
if (marker) {
|
||||
marker.setLatLng(latlng);
|
||||
} else {
|
||||
marker = L.marker(latlng, { draggable: true }).addTo(map);
|
||||
marker.on('dragend', () => {
|
||||
updateInput(marker.getLatLng());
|
||||
});
|
||||
}
|
||||
updateInput(latlng);
|
||||
}
|
||||
|
||||
map.on('click', e => placeOrMoveMarker(e.latlng));
|
||||
|
||||
function submitAnswerMap() {
|
||||
const answer = document.getElementById("location").value;
|
||||
|
||||
if (answer === "") {
|
||||
alert("Bitte einen Ort auf der Karte auswählen!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add grey overlay
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.position = 'fixed';
|
||||
overlay.style.top = '0';
|
||||
overlay.style.left = '0';
|
||||
overlay.style.width = '100%';
|
||||
overlay.style.height = '100%';
|
||||
overlay.style.backgroundColor = 'rgba(128, 128, 128, 0.5)';
|
||||
overlay.style.zIndex = '1000';
|
||||
overlay.style.display = 'flex';
|
||||
overlay.style.justifyContent = 'center';
|
||||
overlay.style.alignItems = 'center';
|
||||
overlay.style.color = 'white';
|
||||
overlay.style.fontSize = '24px';
|
||||
overlay.style.fontWeight = 'bold';
|
||||
overlay.textContent = 'Antwort übermittelt';
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
const now = Date.now();
|
||||
const timeRemaining = Math.max(0, questionDuration - (now - questionStartTime));
|
||||
|
||||
// Submit the answer
|
||||
submitAnswer(answer);
|
||||
}
|
||||
</script>
|
||||
{% elif question_data.type == "map" and request.session.mode != "learn" %}
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2 text-center">Bitte den gesuchten Ort auf der Karte auswählen</p>
|
||||
{% elif question_data.type == "guess" %}
|
||||
{% if request.session.mode == "learn" %}
|
||||
<form class="flex flex-col items-center gap-4 mt-4">
|
||||
<!-- Slider mit Beschriftung -->
|
||||
@@ -193,11 +277,7 @@ class="px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-
|
||||
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
<div class="w-full bg-gray-300 rounded-full h-4 my-4">
|
||||
<div id="time-bar" class="bg-blue-600 h-4 rounded-full transition-all duration-100 linear" style="width: 100%;"></div>
|
||||
</div>
|
||||
|
||||
{% if question_data.type == "multiple_choice" or question_data.type == "true_false" %}
|
||||
{% if question_data.type == "multiple_choice" or question_data.type == "true_false" %}
|
||||
<div class="grid grid-cols-2 gap-4" id="options-container">
|
||||
{% for option in question_data.options %}
|
||||
<button data-index="{{ forloop.counter0 }}"
|
||||
@@ -168,6 +167,58 @@
|
||||
Abschicken
|
||||
</button>
|
||||
</form>
|
||||
{% elif question_data.type == "map" %}
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-2">Ziel auswählen</div>
|
||||
<div id="map" class="h-96"></div>
|
||||
<input type="hidden" value="" id="location" name="location">
|
||||
<div class="mt-4">
|
||||
<button onclick="submitAnswerMap()" class="bg-blue-400 text-white px-4 py-2 rounded-md hover:bg-blue-500 transition-colors">
|
||||
Abgeben
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
let marker = null;
|
||||
const locationInput = document.getElementById('location');
|
||||
|
||||
const map = L.map('map').setView([51.72, 10.45], 3);
|
||||
|
||||
// Prüfe, ob Dark Mode aktiv ist
|
||||
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
// Wähle das passende Karten-Design
|
||||
const tileUrl = isDarkMode
|
||||
? 'https://{s}.basemaps.cartocdn.com/rastertiles/dark_nolabels/{z}/{x}/{y}.png'
|
||||
: 'https://{s}.basemaps.cartocdn.com/rastertiles/light_nolabels/{z}/{x}/{y}.png';
|
||||
|
||||
L.tileLayer(tileUrl, {
|
||||
maxZoom: 19,
|
||||
attribution: '© OpenStreetMap © CARTO'
|
||||
}).addTo(map);
|
||||
|
||||
function updateInput(latlng) {
|
||||
locationInput.value = `${latlng.lat.toFixed(6)},${latlng.lng.toFixed(6)}`;
|
||||
}
|
||||
|
||||
function placeOrMoveMarker(latlng) {
|
||||
if (marker) {
|
||||
marker.setLatLng(latlng);
|
||||
} else {
|
||||
marker = L.marker(latlng, { draggable: true }).addTo(map);
|
||||
|
||||
marker.on('dragend', () => {
|
||||
updateInput(marker.getLatLng());
|
||||
});
|
||||
}
|
||||
updateInput(latlng);
|
||||
}
|
||||
|
||||
map.on('click', e => placeOrMoveMarker(e.latlng));
|
||||
</script>
|
||||
{% else %}
|
||||
<p>aaaaah gibts nicht</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,7 +254,22 @@
|
||||
wsUtil.handleClose(e);
|
||||
};
|
||||
|
||||
function submitAnswer(optionIndex) {
|
||||
function submitAnswer(optionIndex=0, has_options=true) {
|
||||
if (!has_options){
|
||||
answer = document.getElementById("singleanswer").value;
|
||||
|
||||
if (answer == ""){
|
||||
alert("Bitte eine Antwort eingeben!");
|
||||
return;
|
||||
}
|
||||
|
||||
gameSocket.send(JSON.stringify({
|
||||
type: 'submit_answer',
|
||||
participant_id: participantId,
|
||||
answer:answer,
|
||||
time_remaining: questionDuration
|
||||
}));
|
||||
}
|
||||
if (hasAnswered) return;
|
||||
let input = ""; // Declare input here
|
||||
|
||||
@@ -249,7 +315,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function submitAnswerMap() {
|
||||
answer = document.getElementById("location").value;
|
||||
|
||||
if (answer == ""){
|
||||
alert("Bitte eine Antwort eingeben!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add grey overlay
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.position = 'fixed';
|
||||
overlay.style.top = '0';
|
||||
overlay.style.left = '0';
|
||||
overlay.style.width = '100%';
|
||||
overlay.style.height = '100%';
|
||||
overlay.style.backgroundColor = 'rgba(128, 128, 128, 0.5)';
|
||||
overlay.style.zIndex = '1000';
|
||||
overlay.style.display = 'flex';
|
||||
overlay.style.justifyContent = 'center';
|
||||
overlay.style.alignItems = 'center';
|
||||
overlay.style.color = 'white';
|
||||
overlay.style.fontSize = '24px';
|
||||
overlay.style.fontWeight = 'bold';
|
||||
overlay.textContent = 'Antwort übermittelt';
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
const now = Date.now();
|
||||
const timeRemaining = Math.max(0, questionDuration - (now - questionStartTime));
|
||||
|
||||
gameSocket.send(JSON.stringify({
|
||||
type: 'submit_answer',
|
||||
participant_id: participantId,
|
||||
answer: answer,
|
||||
time_remaining: timeRemaining
|
||||
}));
|
||||
}
|
||||
|
||||
// Timer
|
||||
function updateTimer() {
|
||||
|
||||
@@ -9,9 +9,100 @@
|
||||
|
||||
<h2 class="text-xl font-bold mb-4">Aktuelle Frage</h2>
|
||||
<p class="text-lg mb-2">{{ question_data.question }}</p>
|
||||
{% if question_data.type != "order" and question_data.type != "guess" %}
|
||||
|
||||
{% if question_data.type == "map" %}
|
||||
<!-- Map Question Display -->
|
||||
<div class="mb-4">
|
||||
<div id="map" class="h-96 w-full rounded-lg border border-gray-300 dark:border-gray-600"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Parse the target location from the string format "lat,lng"
|
||||
const targetCoords = '{{ question_data.target_location|escapejs }}'.split(',');
|
||||
let targetLat, targetLng;
|
||||
|
||||
if (targetCoords.length === 2) {
|
||||
targetLat = parseFloat(targetCoords[0].trim());
|
||||
targetLng = parseFloat(targetCoords[1].trim());
|
||||
|
||||
// Normalize longitude to be within -180 to 180
|
||||
targetLng = ((targetLng % 360) + 540) % 360 - 180;
|
||||
|
||||
// Clamp latitude to valid range (-90 to 90)
|
||||
targetLat = Math.max(-90, Math.min(90, targetLat));
|
||||
|
||||
console.log('Normalized coordinates:', { lat: targetLat, lng: targetLng });
|
||||
}
|
||||
|
||||
// Validate coordinates
|
||||
if (isNaN(targetLat) || isNaN(targetLng)) {
|
||||
console.warn('Invalid coordinates, hiding map');
|
||||
document.getElementById('map-container').style.display = 'none';
|
||||
return; // Exit the function early
|
||||
}
|
||||
|
||||
const targetLocation = [targetLat, targetLng];
|
||||
console.log('Using coordinates:', targetLocation);
|
||||
|
||||
// Initialize map centered on target location
|
||||
const map = L.map('map').setView(targetLocation, 7);
|
||||
|
||||
// Check for dark mode
|
||||
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
// Choose appropriate map style
|
||||
const tileUrl = isDarkMode
|
||||
? 'https://{s}.basemaps.cartocdn.com/rastertiles/dark_all/{z}/{x}/{y}.png'
|
||||
: 'https://{s}.basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}.png';
|
||||
|
||||
L.tileLayer(tileUrl, {
|
||||
maxZoom: 19,
|
||||
attribution: '© OpenStreetMap © CARTO',
|
||||
noWrap: true
|
||||
}).addTo(map);
|
||||
|
||||
// Add target marker
|
||||
L.marker(targetLocation, {
|
||||
icon: L.divIcon({
|
||||
html: '🎯',
|
||||
className: 'text-3xl',
|
||||
iconSize: [32, 32],
|
||||
iconAnchor: [16, 32],
|
||||
popupAnchor: [0, -32]
|
||||
})
|
||||
}).addTo(map)
|
||||
.bindPopup('Zielort')
|
||||
.openPopup();
|
||||
|
||||
// Add tolerance radius circle if available
|
||||
{% if question_data.tolerance_radius %}
|
||||
(function() {
|
||||
try {
|
||||
const toleranceRadius = parseFloat('{{ question_data.tolerance_radius|escapejs }}'.replace(',', '.'));
|
||||
if (!isNaN(toleranceRadius) && toleranceRadius > 0) {
|
||||
L.circle(targetLocation, {
|
||||
color: isDarkMode ? '#3b82f6' : '#2563eb',
|
||||
fillColor: isDarkMode ? '#3b82f6' : '#2563eb',
|
||||
fillOpacity: 0.2,
|
||||
radius: toleranceRadius
|
||||
}).addTo(map);
|
||||
console.log('Added tolerance radius:', toleranceRadius);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error adding tolerance radius:', e);
|
||||
}
|
||||
})();
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% elif question_data.type != "order" and question_data.type != "guess" %}
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
|
||||
{% for option in question_data.options %}
|
||||
|
||||
<div id="option-box-{{ forloop.counter0 }}" class=" dark:bg-transparent relative p-4 rounded-lg overflow-auto break-words hyphens-auto {% if option.is_correct %} border-2 border-green-500{% else %}border-2 border-red-500{% endif %}">
|
||||
@@ -25,7 +116,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elif question_data.type == "guess" %}
|
||||
{% elif question_data.type == "guess" %}
|
||||
<div id="option-box-0" class="relative p-4 rounded-lg border-2 border-green-500 overflow-auto">
|
||||
<div id="option-fill-0" class="absolute left-0 top-0 h-full bg-green-300 opacity-50 z-0 transition-all duration-1500" style="width: 0%;"></div>
|
||||
<div class="relative z-10">
|
||||
|
||||
Reference in New Issue
Block a user