Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Cloze Reader</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link href="https://fonts.googleapis.com/css2?family=Special+Elite&display=swap" rel="stylesheet"> | |
<style> | |
body { font-family: 'Special Elite', monospace; background: #faf7f0; } | |
.paper-sheet { background: #fefcf7; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } | |
.cloze-input { border: none; border-bottom: 2px solid #000; background: transparent; text-align: center; font-family: inherit; } | |
.typewriter-button { background: #f5f1e8; border: 2px solid #000; font-family: inherit; padding: 8px 16px; cursor: pointer; } | |
.correct { background-color: rgba(16, 185, 129, 0.1); border-color: #10b981; } | |
.incorrect { background-color: rgba(239, 68, 68, 0.1); border-color: #ef4444; } | |
</style> | |
</head> | |
<body class="min-h-screen p-4"> | |
<div class="max-w-4xl mx-auto"> | |
<header class="text-center mb-8"> | |
<div class="flex items-center justify-center gap-3 mb-2"> | |
<span class="text-4xl">π</span> | |
<h1 class="text-4xl font-bold">Cloze Reader</h1> | |
</div> | |
<p class="text-gray-600">Fill in the blanks to practice reading comprehension</p> | |
</header> | |
<main id="game-container"> | |
<div id="loading" class="text-center py-8"> | |
<p class="text-lg">Loading passages...</p> | |
</div> | |
<div id="game-area" class="paper-sheet rounded-lg p-6 hidden"> | |
<div id="book-info" class="text-sm italic text-gray-600 mb-4"></div> | |
<div id="round-info" class="text-sm bg-amber-100 px-3 py-1 rounded-full inline-block mb-4"></div> | |
<div id="contextualization" class="bg-amber-50 border-l-4 border-amber-500 p-3 mb-4 text-sm"></div> | |
<div id="passage-content" class="text-lg leading-relaxed mb-6"></div> | |
<div id="hints-section" class="bg-yellow-50 border-l-4 border-yellow-500 p-3 mb-4 hidden"> | |
<div class="font-semibold mb-2 text-sm">π‘ Hints:</div> | |
<div id="hints-list" class="text-sm space-y-1"></div> | |
</div> | |
<div class="flex gap-4 justify-center flex-wrap"> | |
<button id="submit-btn" class="typewriter-button">Submit</button> | |
<button id="next-btn" class="typewriter-button hidden">Next Passage</button> | |
<button id="hint-btn" class="typewriter-button">Show Hints</button> | |
</div> | |
<div id="result" class="mt-4 text-center font-semibold"></div> | |
</div> | |
</main> | |
</div> | |
<script> | |
// Simple cloze reader implementation | |
class SimpleClozeReader { | |
constructor() { | |
this.currentLevel = 1; | |
this.currentRound = 1; | |
this.blanks = []; | |
this.hints = []; | |
this.books = [ | |
{ | |
title: "Pride and Prejudice", | |
author: "Jane Austen", | |
text: "It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife. However little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters." | |
}, | |
{ | |
title: "The Adventures of Tom Sawyer", | |
author: "Mark Twain", | |
text: "Tom! No answer. Tom! No answer. What's gone with that boy, I wonder? You TOM! No answer. The old lady pulled her spectacles down and looked over them about the room; then she put them up and looked out under them. She seldom or never looked through them for so small a thing as a boy." | |
} | |
]; | |
this.init(); | |
} | |
init() { | |
this.setupEventListeners(); | |
this.startNewRound(); | |
} | |
setupEventListeners() { | |
document.getElementById('submit-btn').onclick = () => this.submitAnswers(); | |
document.getElementById('next-btn').onclick = () => this.nextRound(); | |
document.getElementById('hint-btn').onclick = () => this.toggleHints(); | |
} | |
startNewRound() { | |
const book = this.books[Math.floor(Math.random() * this.books.length)]; | |
const blanksCount = this.currentLevel <= 2 ? 1 : this.currentLevel <= 4 ? 2 : 3; | |
// Simple word selection | |
const words = book.text.split(' '); | |
const selectedIndices = []; | |
const contentWords = ['truth', 'man', 'fortune', 'wife', 'feelings', 'neighbourhood', 'families', 'daughters', 'answer', 'boy', 'lady', 'spectacles', 'room']; | |
for (let i = 0; i < blanksCount && i < contentWords.length; i++) { | |
const wordIndex = words.findIndex(w => w.toLowerCase().includes(contentWords[i])); | |
if (wordIndex !== -1) selectedIndices.push(wordIndex); | |
} | |
// Create blanks | |
this.blanks = selectedIndices.map((index, i) => ({ | |
index: i, | |
originalWord: words[index].replace(/[^\w]/g, ''), | |
wordIndex: index | |
})); | |
// Create hints | |
this.hints = this.blanks.map((blank, i) => { | |
const word = blank.originalWord; | |
if (this.currentLevel <= 2) { | |
return `${word.length} letters, starts with "${word[0]}", ends with "${word[word.length-1]}"`; | |
} else { | |
return `${word.length} letters, starts with "${word[0]}"`; | |
} | |
}); | |
// Create display text | |
let displayText = book.text; | |
this.blanks.forEach((blank, i) => { | |
const word = words[blank.wordIndex]; | |
const input = `<input type="text" class="cloze-input w-20 mx-1" data-index="${i}" placeholder="${'_'.repeat(Math.max(3, blank.originalWord.length))}">`; | |
displayText = displayText.replace(word, input); | |
}); | |
// Update UI | |
document.getElementById('book-info').innerHTML = `<strong>${book.title}</strong> by ${book.author}`; | |
document.getElementById('round-info').textContent = `Level ${this.currentLevel} β’ ${blanksCount} blank${blanksCount > 1 ? 's' : ''}`; | |
document.getElementById('contextualization').innerHTML = `π Practice with classic literature from ${book.author}'s "${book.title}"`; | |
document.getElementById('passage-content').innerHTML = displayText; | |
document.getElementById('hints-list').innerHTML = this.hints.map((hint, i) => `<div>${i+1}. ${hint}</div>`).join(''); | |
// Show game area | |
document.getElementById('loading').classList.add('hidden'); | |
document.getElementById('game-area').classList.remove('hidden'); | |
document.getElementById('hints-section').classList.add('hidden'); | |
document.getElementById('result').textContent = ''; | |
document.getElementById('submit-btn').classList.remove('hidden'); | |
document.getElementById('next-btn').classList.add('hidden'); | |
} | |
submitAnswers() { | |
const inputs = document.querySelectorAll('.cloze-input'); | |
let correct = 0; | |
inputs.forEach((input, i) => { | |
const userAnswer = input.value.trim().toLowerCase(); | |
const correctAnswer = this.blanks[i].originalWord.toLowerCase(); | |
if (userAnswer === correctAnswer) { | |
input.classList.add('correct'); | |
correct++; | |
} else { | |
input.classList.add('incorrect'); | |
// Show correct answer | |
const span = document.createElement('span'); | |
span.className = 'text-green-600 font-semibold ml-2 text-sm'; | |
span.textContent = `β ${this.blanks[i].originalWord}`; | |
input.parentNode.appendChild(span); | |
} | |
input.disabled = true; | |
}); | |
const percentage = Math.round((correct / this.blanks.length) * 100); | |
const passed = correct >= (this.blanks.length === 1 ? 1 : this.blanks.length - 1); | |
let message = `Score: ${correct}/${this.blanks.length} (${percentage}%)`; | |
if (passed) { | |
message += ` - Excellent! Advancing to Level ${this.currentLevel + 1}! π`; | |
document.getElementById('result').className = 'mt-4 text-center font-semibold text-green-600'; | |
this.currentLevel++; | |
} else { | |
message += ` - Keep practicing! πͺ`; | |
document.getElementById('result').className = 'mt-4 text-center font-semibold text-red-600'; | |
} | |
document.getElementById('result').textContent = message; | |
document.getElementById('submit-btn').classList.add('hidden'); | |
document.getElementById('next-btn').classList.remove('hidden'); | |
this.currentRound++; | |
} | |
nextRound() { | |
document.querySelectorAll('.cloze-input').forEach(input => { | |
input.classList.remove('correct', 'incorrect'); | |
input.disabled = false; | |
input.value = ''; | |
}); | |
document.querySelectorAll('.text-green-600').forEach(el => el.remove()); | |
this.startNewRound(); | |
} | |
toggleHints() { | |
const hintsSection = document.getElementById('hints-section'); | |
const btn = document.getElementById('hint-btn'); | |
if (hintsSection.classList.contains('hidden')) { | |
hintsSection.classList.remove('hidden'); | |
btn.textContent = 'Hide Hints'; | |
} else { | |
hintsSection.classList.add('hidden'); | |
btn.textContent = 'Show Hints'; | |
} | |
} | |
} | |
// Initialize when page loads | |
document.addEventListener('DOMContentLoaded', () => { | |
new SimpleClozeReader(); | |
}); | |
</script> | |
</body> | |
</html> |