body {
font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;
max-width: 100%;
margin: 0 auto;
padding: 20px;
background-color: #f9f9f9;
}
h1, h2, h3 {
color: #2c3e50;
}
.container {
background-color: white;
padding: 30px;
max-width: 100%;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.subscription-form {
background-color: #e8f4f8;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.exercise-container {
display: none;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 12px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
button:hover {
background-color: #2980b9;
}
.exercise-text {
line-height: 2;
font-size: 18px;
}
.gap {
border-bottom: 1px solid #333;
padding: 0 5px;
margin: 0 3px;
min-width: 50px;
display: inline-block;
}
.gender-selector {
width: 80px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 0 5px;
font-size: 16px;
}
.gender-selector.correct {
background-color: #d4edda;
border-color: #c3e6cb;
}
.gender-selector.incorrect {
background-color: #f8d7da;
border-color: #f5c6cb;
}
.result {
margin-top: 30px;
padding: 20px;
background-color: #eafaf1;
border-radius: 8px;
display: none;
}
.certificate {
text-align: center;
margin-top: 30px;
padding: 20px;
border: 2px solid #2c3e50;
border-radius: 8px;
display: none;
}
.difficulty-selector {
margin-bottom: 20px;
}
.error {
color: red;
font-size: 14px;
}
#certificatePreview {
position: relative;
width: 100%;
height: 450px;
background-color: #fff;
border: 10px solid transparent;
border-image: linear-gradient(45deg, #f39c12, #e67e22, #d35400, #c0392b, #e74c3c) 1;
box-sizing: border-box;
overflow: hidden;
}
.certificate-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.07;
z-index: 0;
background-position: center;
background-repeat: no-repeat;
background-size: 70%;
}
.certificate-content {
position: relative;
z-index: 1;
padding: 20px;
width: 100%;
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.certificate-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.certificate-title {
flex-grow: 1;
text-align: center;
}
.certificate-logo {
width: 120px;
height: 60px;
}
.certificate-footer {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.certificate-qr {
width: 120px;
height: 120px;
display: flex;
justify-content: center;
align-items: center;
}
.certificate-qr img {
max-width: 100%;
max-height: 100%;
}
.certificate-signature {
flex-grow: 1;
text-align: center;
}
.triangle-pattern {
position: absolute;
width: 150px;
height: 120px;
}
.triangle-pattern.top-right {
top: 0;
right: 0;
}
.triangle-pattern.bottom-left {
bottom: 0;
left: 0;
}
.info-tooltip {
display: inline-block;
position: relative;
margin-left: 5px;
cursor: help;
}
.info-tooltip:after {
content: “?”;
display: inline-block;
width: 18px;
height: 18px;
line-height: 18px;
text-align: center;
border-radius: 50%;
background-color: #3498db;
color: white;
font-weight: bold;
font-size: 14px;
}
.info-tooltip .tooltip-text {
visibility: hidden;
width: 250px;
background-color: #2c3e50;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -125px;
opacity: 0;
transition: opacity 0.3s;
}
.info-tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
/* Añadir un indicador de carga para la descarga del PDF */
.loading {
display: none;
margin-top: 15px;
font-style: italic;
color: #666;
}
Exercice de Grammaire: Le Genre des Noms en Français
Inscrivez-vous pour commencer l’exercice
Veuillez vous abonner et remplir vos informations pour accéder à l’exercice.
Identifiez le genre des noms en français
Choisissez le niveau de difficulté:
Facile
Moyen
Difficile
Pour chaque nom, sélectionnez si le mot est masculin (M) ou féminin (F).
Le Genre des Noms en Français (Niveau Facile)
1. livre
—
M
F
2. table
—
M
F
3. maison
—
M
F
4. jardin
—
M
F
5. fleur
—
M
F
6. arbre
—
M
F
7. voiture
—
M
F
8. vélo
—
M
F
9. chaise
—
M
F
10. stylo
—
M
F
11. école
—
M
F
12. pain
—
M
F
13. eau
—
M
F
14. lit
—
M
F
15. rue
—
M
F
16. soleil
—
M
F
17. lune
—
M
F
18. train
—
M
F
19. pomme
—
M
F
20. café
—
M
F
21. plage
—
M
F
22. chapeau
—
M
F
23. porte
—
M
F
24. mur
—
M
F
25. fenêtre
—
M
F
Le Genre des Noms en Français (Niveau Moyen)
1. courage
—
M
F
2. patience
—
M
F
3. bonheur
—
M
F
4. joie
—
M
F
5. silence
—
M
F
6. sagesse
—
M
F
7. squelette
—
M
F
8. dent
—
M
F
9. téléphone
—
M
F
10. photographie
—
M
F
11. méthode
—
M
F
12. système
—
M
F
13. folie
—
M
F
14. génie
—
M
F
15. orchestre
—
M
F
16. mélodie
—
M
F
17. incendie
—
M
F
18. alarme
—
M
F
19. dentifrice
—
M
F
20. horloge
—
M
F
21. escalier
—
M
F
22. planète
—
M
F
23. bruit
—
M
F
24. chanson
—
M
F
25. paradoxe
—
M
F
Le Genre des Noms en Français (Niveau Difficile)
1. éclair
—
M
F
2. primeur
—
M
F
3. pétale
—
M
F
4. orbite
—
M
F
5. indice
—
M
F
6. enzyme
—
M
F
7. alvéole
—
M
F
8. hémisphère
—
M
F
9. météorite
—
M
F
10. équinoxe
—
M
F
11. hostie
—
M
F
12. tentacule
—
M
F
13. artère
—
M
F
14. abîme
—
M
F
15. oasis
—
M
F
16. épiderme
—
M
F
17. énigme
—
M
F
18. pamplemousse
—
M
F
19. effluve
—
M
F
20. aphte
—
M
F
21. échappatoire
—
M
F
22. anagramme
—
M
F
23. antidote
—
M
F
24. entracte
—
M
F
25. apostrophe
—
M
F
Résultats
Certificat de réussite
Certificat d’Excellence
Maîtrise du Genre des Noms en Français
Ce certificat est décerné à
pour avoir complété avec succès l’exercice sur
le genre des noms en français au niveau
avec un score de
Date:
document.addEventListener(‘DOMContentLoaded’, function() {
// Asegurar que jsPDF está disponible
if (typeof window.jspdf === ‘undefined’) {
window.jspdf = window.jsPDF;
}
// Réferences aux éléments DOM
const subscriptionForm = document.getElementById(‘subscriptionForm’);
const exerciseContainer = document.getElementById(‘exerciseContainer’);
const startButton = document.getElementById(‘startButton’);
const verifyButton = document.getElementById(‘verifyButton’);
const difficultySelect = document.getElementById(‘difficultySelect’);
const changeDifficultyButton = document.getElementById(‘changeDifficultyButton’);
const resultContainer = document.getElementById(‘resultContainer’);
const resultText = document.getElementById(‘resultText’);
const mistakesContainer = document.getElementById(‘mistakesContainer’);
const showCertificateButton = document.getElementById(‘showCertificateButton’);
const certificateContainer = document.getElementById(‘certificateContainer’);
const downloadCertificateButton = document.getElementById(‘downloadCertificateButton’);
const pdfLoading = document.getElementById(‘pdfLoading’);
// Informations de l’utilisateur
let userData = {
prenom: ”,
nom: ”,
email: ”,
difficulty: ‘facile’,
score: 0
};
// Fonction pour afficher l’exercice selon le niveau de difficulté
function showExerciseByDifficulty(difficulty) {
// Masquer tous les exercices
document.querySelectorAll(‘.exercise-level’).forEach(el => {
el.style.display = ‘none’;
});
// Afficher l’exercice correspondant à la difficulté
const exerciseId = ‘exercise’ + difficulty.charAt(0).toUpperCase() + difficulty.slice(1);
document.getElementById(exerciseId).style.display = ‘block’;
userData.difficulty = difficulty;
}
// Démarrer l’exercice
startButton.addEventListener(‘click’, function() {
// Valider le formulaire
const prenom = document.getElementById(‘prenom’).value.trim();
const nom = document.getElementById(‘nom’).value.trim();
const email = document.getElementById(‘email’).value.trim();
const subscribe = document.getElementById(‘subscribe’).checked;
// Réinitialiser les erreurs
document.getElementById(‘prenomError’).textContent = ”;
document.getElementById(‘nomError’).textContent = ”;
document.getElementById(‘emailError’).textContent = ”;
document.getElementById(‘subscribeError’).textContent = ”;
let isValid = true;
// Vérification des champs
if (!prenom) {
document.getElementById(‘prenomError’).textContent = ‘Veuillez entrer votre prénom’;
isValid = false;
}
if (!nom) {
document.getElementById(‘nomError’).textContent = ‘Veuillez entrer votre nom’;
isValid = false;
}
if (!email) {
document.getElementById(‘emailError’).textContent = ‘Veuillez entrer votre email’;
isValid = false;
} else if (!isValidEmail(email)) {
document.getElementById(‘emailError’).textContent = ‘Veuillez entrer un email valide’;
isValid = false;
}
if (!subscribe) {
document.getElementById(‘subscribeError’).textContent = ‘Veuillez cocher cette case pour continuer’;
isValid = false;
}
if (isValid) {
// Enregistrer les données utilisateur
userData.prenom = prenom;
userData.nom = nom;
userData.email = email;
// Afficher l’exercice et masquer le formulaire
subscriptionForm.style.display = ‘none’;
exerciseContainer.style.display = ‘block’;
// Afficher l’exercice selon la difficulté sélectionnée
showExerciseByDifficulty(difficultySelect.value);
}
});
// Changer la difficulté
changeDifficultyButton.addEventListener(‘click’, function() {
showExerciseByDifficulty(difficultySelect.value);
// Réinitialiser les résultats
resultContainer.style.display = ‘none’;
certificateContainer.style.display = ‘none’;
// Réinitialiser les sélections
document.querySelectorAll(‘.gender-selector’).forEach(selector => {
selector.value = ”;
selector.classList.remove(‘correct’, ‘incorrect’);
});
});
// Vérifier les réponses
verifyButton.addEventListener(‘click’, function() {
// Obtenir les sélecteurs du niveau actuel
const activeExercises = {
‘facile’: document.getElementById(‘exerciseFacile’),
‘moyen’: document.getElementById(‘exerciseMoyen’),
‘difficile’: document.getElementById(‘exerciseDifficile’)
};
const currentLevel = activeExercises[userData.difficulty];
const selectors = currentLevel.querySelectorAll(‘.gender-selector’);
let allAnswered = true;
selectors.forEach(selector => {
if (selector.value === ”) {
allAnswered = false;
}
});
if (!allAnswered) {
alert(‘Veuillez répondre à toutes les questions avant de vérifier vos réponses.’);
return;
}
// Compter les réponses correctes
let correctCount = 0;
let mistakes = [];
selectors.forEach(selector => {
const userAnswer = selector.value;
const correctAnswer = selector.getAttribute(‘data-answer’);
const questionNumber = selector.getAttribute(‘data-number’);
// Obtenir le mot (précédent élément strong)
const wordElement = selector.previousElementSibling;
const word = wordElement ? wordElement.textContent.trim() : `Question ${questionNumber}`;
if (userAnswer === correctAnswer) {
correctCount++;
selector.classList.add(‘correct’);
selector.classList.remove(‘incorrect’);
} else {
selector.classList.add(‘incorrect’);
selector.classList.remove(‘correct’);
mistakes.push({
number: questionNumber,
word: word,
userAnswer: userAnswer,
correctAnswer: correctAnswer
});
}
});
// Calculer le score
const totalQuestions = selectors.length;
const scorePercentage = Math.round((correctCount / totalQuestions) * 100);
userData.score = scorePercentage;
// Afficher les résultats
resultText.innerHTML = `Votre score: ${correctCount} / ${totalQuestions} (${scorePercentage}%)`;
// Afficher les erreurs
mistakesContainer.innerHTML = ”;
if (mistakes.length > 0) {
const mistakeTitle = document.createElement(‘h4’);
mistakeTitle.textContent = ‘Erreurs à corriger:’;
mistakesContainer.appendChild(mistakeTitle);
const mistakeList = document.createElement(‘ul’);
mistakes.forEach(mistake => {
const li = document.createElement(‘li’);
li.innerHTML = `Question ${mistake.number}: ${mistake.word} – Vous avez répondu “${mistake.userAnswer}”, mais la réponse correcte est “${mistake.correctAnswer}”.`;
mistakeList.appendChild(li);
});
mistakesContainer.appendChild(mistakeList);
} else {
const perfectScore = document.createElement(‘p’);
perfectScore.innerHTML = “Félicitations ! Vous avez tout correct !”;
mistakesContainer.appendChild(perfectScore);
}
// Montrer le bouton pour le certificat si le score est bon
if (scorePercentage >= 70) {
showCertificateButton.style.display = ‘block’;
} else {
showCertificateButton.style.display = ‘none’;
}
resultContainer.style.display = ‘block’;
});
// Afficher le certificat
showCertificateButton.addEventListener(‘click’, function() {
// Mettre à jour le certificat avec les informations de l’utilisateur
document.getElementById(‘certificateName’).textContent = `${userData.prenom} ${userData.nom}`;
document.getElementById(‘certificateLevel’).textContent = userData.difficulty;
document.getElementById(‘certificateScore’).textContent = `${userData.score}%`;
// Date actuelle pour le certificat
const today = new Date();
const options = { year: ‘numeric’, month: ‘long’, day: ‘numeric’ };
document.getElementById(‘certificateDate’).textContent = today.toLocaleDateString(‘fr-FR’, options);
// Générer un QR code simple pour le certificat (simpler placeholder)
const qrCodeElement = document.querySelector(‘.certificate-qr’);
qrCodeElement.innerHTML = ``;
// Ajouter une image de fond pour le certificat
document.querySelector(‘.certificate-background’).style.backgroundImage = “url(‘/api/placeholder/300/300’)”;
// Simuler quelques motifs triangulaires
const trianglePatterns = document.querySelectorAll(‘.triangle-pattern’);
trianglePatterns.forEach(pattern => {
pattern.style.backgroundImage = “url(‘/api/placeholder/150/120’)”;
});
// Afficher le conteneur du certificat
certificateContainer.style.display = ‘block’;
// Faire défiler jusqu’au certificat
certificateContainer.scrollIntoView({ behavior: ‘smooth’ });
});
// Télécharger le certificat en PDF
downloadCertificateButton.addEventListener(‘click’, function() {
// Mostrar indicador de carga
pdfLoading.style.display = ‘block’;
// Obtener el elemento del certificado para convertirlo en una imagen
const certificateElement = document.getElementById(‘certificatePreview’);
// Usar html2canvas para convertir el certificado en un canvas
html2canvas(certificateElement, {
scale: 2, // Mejor calidad
useCORS: true,
allowTaint: true,
backgroundColor: ‘#ffffff’
}).then(function(canvas) {
// Crear un nuevo documento PDF
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({
orientation: ‘landscape’,
unit: ‘mm’,
format: ‘a4’
});
// Tamaño de página A4 apaisado: 297 x 210 mm
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
// Convertir el canvas a una imagen
const imgData = canvas.toDataURL(‘image/jpeg’, 1.0);
// Calcular dimensiones para que la imagen se ajuste manteniendo proporciones
const imgWidth = canvas.width;
const imgHeight = canvas.height;
const ratio = Math.min(pageWidth / imgWidth, pageHeight / imgHeight);
const imgX = (pageWidth – imgWidth * ratio) / 2;
const imgY = (pageHeight – imgHeight * ratio) / 2;
// Añadir la imagen al PDF
pdf.addImage(imgData, ‘JPEG’, imgX, imgY, imgWidth * ratio, imgHeight * ratio);
// Nombre del archivo
const level = userData.difficulty.charAt(0).toUpperCase() + userData.difficulty.slice(1);
const fileName = `Certificat_${level}_${userData.prenom}_${userData.nom}.pdf`;
// Guardar el PDF
pdf.save(fileName);
// Ocultar indicador de carga
pdfLoading.style.display = ‘none’;
}).catch(function(error) {
console.error(‘Error al generar el PDF:’, error);
alert(‘Une erreur est survenue lors de la génération du PDF. Veuillez réessayer.’);
pdfLoading.style.display = ‘none’;
});
});
// Fonction pour valider l’email
function isValidEmail(email) {
const re = /^[^s@]+@[^s@]+.[^s@]+$/;
return re.test(email);
}
});