Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion alphabetizer-lab.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,13 @@ <h2>Upload Text File</h2>
</div>

<div class="results-section" id="resultsSection" style="display: none;">
<div class="results-header" id="resultsHeader">Word Analysis Results</div>
<div class="output-header">
<div class="results-header" id="resultsHeader">Word Analysis Results</div>
<div class="font-size-control">
<label for="fontSizeSlider">Font Size:</label>
<input type="range" id="fontSizeSlider" class="slider" min="10" max="28" value="16" step="1">
</div>
</div>
<div class="word-display" id="wordDisplay"></div>
<div class="stats" id="statsDisplay"></div>
</div>
Expand Down Expand Up @@ -213,6 +219,15 @@ <h2>Upload Text File</h2>
resultsSection.style.display = 'block';
resultsSection.scrollIntoView({ behavior: 'smooth' });
}

// Font size slider
const fontSizeSlider = document.getElementById('fontSizeSlider');
const wordDisplay = document.getElementById('wordDisplay');
if (fontSizeSlider && wordDisplay) {
fontSizeSlider.addEventListener('input', (e) => {
wordDisplay.style.fontSize = e.target.value + 'px';
});
}
</script>
</body>
</html>
81 changes: 52 additions & 29 deletions grid-it-lab.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,16 @@ <h3>Grid Dimensions</h3>

<div class="option-group words-per-cell-group">
<h3>Words Per Cell</h3>
<div class="radio-group">
<label class="radio-option" for="oneWord">
<input type="radio" id="oneWord" name="wordsPerCell" value="1" checked />
<span>1 Word</span>
</label>
<label class="radio-option" for="twoWords">
<input type="radio" id="twoWords" name="wordsPerCell" value="2" />
<span>2 Words</span>
</label>
<label class="radio-option" for="threeWords">
<input type="radio" id="threeWords" name="wordsPerCell" value="3" />
<span>3 Words</span>
</label>
<label class="radio-option" for="fitAll">
<input type="radio" id="fitAll" name="wordsPerCell" value="fit" />
<span>Fit All (divide evenly)</span>
</label>
<div class="checkbox-container">
<input type="checkbox" id="fitAllCheckbox" />
<label for="fitAllCheckbox">Fit All (divide evenly across grid)</label>
</div>
<div class="slider-container" id="wordsPerCellContainer">
<div class="slider-label">
<span>Words per cell:</span>
<span id="wordsPerCellValue">1</span>
</div>
<input type="range" id="wordsPerCellSlider" class="slider" min="1" max="20" value="1" step="1">
</div>
</div>

Expand All @@ -155,6 +148,10 @@ <h3>Reading Direction</h3>
<div class="results-section" id="resultsSection" style="display: none;">
<div class="output-header">
<div class="output-title">Grid View</div>
<div class="font-size-control">
<label for="fontSizeSlider">Font Size:</label>
<input type="range" id="fontSizeSlider" class="slider" min="10" max="24" value="14" step="1">
</div>
</div>
<div id="gridContainer"></div>
<div class="stats" id="statsDisplay"></div>
Expand Down Expand Up @@ -226,27 +223,22 @@ <h3>Reading Direction</h3>

const columnsInput = document.getElementById('columnsInput').value;
const rowsInput = document.getElementById('rowsInput').value;
const wordsPerCellValue = document.querySelector('input[name="wordsPerCell"]:checked').value;
const fitAllChecked = document.getElementById('fitAllCheckbox').checked;
const wordsPerCellSliderValue = parseInt(document.getElementById('wordsPerCellSlider').value);
const direction = document.querySelector('input[name="direction"]:checked').value;

let columns, rows, wordsPerCell;

// Determine words per cell
if (wordsPerCellValue === 'fit') {
wordsPerCell = null; // Will calculate later
} else {
wordsPerCell = parseInt(wordsPerCellValue);
}

// Calculate grid dimensions
if (wordsPerCellValue === 'fit') {
if (fitAllChecked) {
// "Fit All" mode: divide words evenly
columns = columnsInput ? parseInt(columnsInput) : 10;
rows = rowsInput ? parseInt(rowsInput) : Math.ceil(words.length / columns);
const totalCells = columns * rows;
wordsPerCell = Math.ceil(words.length / totalCells);
} else {
// Specific words per cell
// Specific words per cell from slider
wordsPerCell = wordsPerCellSliderValue;
const totalWordsNeeded = words.length;
const cellsNeeded = Math.ceil(totalWordsNeeded / wordsPerCell);

Expand Down Expand Up @@ -313,14 +305,14 @@ <h3>Reading Direction</h3>
gridContainer.appendChild(table);

// Calculate statistics
const wordsUsed = wordsPerCellValue === 'fit'
const wordsUsed = fitAllChecked
? words.length
: Math.min(words.length, totalCells * wordsPerCell);
const wordsNotUsed = words.length - wordsUsed;

statsDisplay.innerHTML = `
<strong>Grid Size:</strong> ${columns} columns × ${rows} rows (${totalCells} cells)<br>
<strong>Words Per Cell:</strong> ${wordsPerCellValue === 'fit' ? `~${wordsPerCell} (average)` : wordsPerCell}<br>
<strong>Words Per Cell:</strong> ${fitAllChecked ? `~${wordsPerCell} (average)` : wordsPerCell}<br>
<strong>Total Words:</strong> ${words.length}<br>
<strong>Words in Grid:</strong> ${wordsUsed}<br>
${wordsNotUsed > 0 ? `<strong>Words Not Displayed:</strong> ${wordsNotUsed}<br>` : ''}
Expand All @@ -330,6 +322,37 @@ <h3>Reading Direction</h3>
resultsSection.style.display = 'block';
resultsSection.scrollIntoView({ behavior: 'smooth' });
}

// Words per cell slider
const wordsPerCellSlider = document.getElementById('wordsPerCellSlider');
const wordsPerCellValue = document.getElementById('wordsPerCellValue');
const wordsPerCellContainer = document.getElementById('wordsPerCellContainer');
const fitAllCheckbox = document.getElementById('fitAllCheckbox');

if (wordsPerCellSlider && wordsPerCellValue) {
wordsPerCellSlider.addEventListener('input', (e) => {
wordsPerCellValue.textContent = e.target.value;
});
}

if (fitAllCheckbox && wordsPerCellContainer) {
fitAllCheckbox.addEventListener('change', (e) => {
wordsPerCellContainer.style.opacity = e.target.checked ? '0.5' : '1';
wordsPerCellSlider.disabled = e.target.checked;
});
}

// Font size slider
const fontSizeSlider = document.getElementById('fontSizeSlider');
const gridContainer = document.getElementById('gridContainer');
if (fontSizeSlider && gridContainer) {
fontSizeSlider.addEventListener('input', (e) => {
const table = gridContainer.querySelector('.grid-table');
if (table) {
table.style.fontSize = e.target.value + 'px';
}
});
}
</script>
</body>
</html>
110 changes: 89 additions & 21 deletions lipogram-lab.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ <h2>Upload Text File</h2>
</div>

<div class="options-section" id="optionsSection" style="display: none;">
<div class="alphabet-section">
<h3>Filter Mode</h3>
<div class="mode-buttons">
<button class="mode-btn active" id="removeModeBtn">Remove words WITH selected letters</button>
<button class="mode-btn" id="keepModeBtn">Keep ONLY words WITH selected letters</button>
</div>
</div>

<div class="alphabet-section">
<h3>English Alphabet</h3>
<div class="select-controls">
Expand All @@ -119,13 +127,15 @@ <h3>English Alphabet</h3>
<h3>Spanish Letter</h3>
<div class="letter-toggles" id="spanishToggles"></div>
</div>

<button class="apply-btn" id="applyBtn">Apply Lipogram Filter</button>
</div>

<div class="results-section" id="resultsSection" style="display: none;">
<div class="output-header">
<div class="output-title">Filtered Text</div>
<div class="font-size-control">
<label for="fontSizeSlider">Font Size:</label>
<input type="range" id="fontSizeSlider" class="slider" min="12" max="32" value="16" step="1">
</div>
</div>
<div class="output-text" id="outputText"></div>
<div class="stats" id="statsDisplay"></div>
Expand All @@ -135,13 +145,15 @@ <h3>Spanish Letter</h3>
<script>
let fileContent = '';
let selectedLetters = new Set();
let filterMode = 'remove'; // 'remove' or 'keep'

const fileInput = document.getElementById('textFile');
const optionsSection = document.getElementById('optionsSection');
const applyBtn = document.getElementById('applyBtn');
const resultsSection = document.getElementById('resultsSection');
const outputText = document.getElementById('outputText');
const statsDisplay = document.getElementById('statsDisplay');
const removeModeBtn = document.getElementById('removeModeBtn');
const keepModeBtn = document.getElementById('keepModeBtn');

const englishLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const spanishLetters = ['Ñ'];
Expand Down Expand Up @@ -180,15 +192,41 @@ <h3>Spanish Letter</h3>
selectedLetters.add(letter);
element.classList.add('selected');
}
// Auto-apply filter when letters are toggled
if (fileContent) {
applyLipogramFilter();
}
}

// Mode button handlers
removeModeBtn.addEventListener('click', () => {
filterMode = 'remove';
removeModeBtn.classList.add('active');
keepModeBtn.classList.remove('active');
if (fileContent && selectedLetters.size > 0) {
applyLipogramFilter();
}
});

keepModeBtn.addEventListener('click', () => {
filterMode = 'keep';
keepModeBtn.classList.add('active');
removeModeBtn.classList.remove('active');
if (fileContent && selectedLetters.size > 0) {
applyLipogramFilter();
}
});

// Select/Deselect all English letters
document.getElementById('selectAllEnglish').addEventListener('click', () => {
englishLetters.forEach(letter => {
selectedLetters.add(letter);
const toggle = document.querySelector(`#englishToggles [data-letter="${letter}"]`);
if (toggle) toggle.classList.add('selected');
});
if (fileContent) {
applyLipogramFilter();
}
});

document.getElementById('deselectAllEnglish').addEventListener('click', () => {
Expand All @@ -197,10 +235,12 @@ <h3>Spanish Letter</h3>
const toggle = document.querySelector(`#englishToggles [data-letter="${letter}"]`);
if (toggle) toggle.classList.remove('selected');
});
if (fileContent) {
applyLipogramFilter();
}
});

fileInput.addEventListener('change', handleFileUpload);
applyBtn.addEventListener('click', applyLipogramFilter);

function handleFileUpload(event) {
const file = event.target.files[0];
Expand All @@ -212,7 +252,10 @@ <h3>Spanish Letter</h3>
fileContent = e.target.result;
optionsSection.style.display = 'block';
resultsSection.style.display = 'none';
applyBtn.disabled = false;
// Auto-apply if letters are already selected
if (selectedLetters.size > 0) {
applyLipogramFilter();
}
};
reader.onerror = function(e) {
console.error('Error reading file:', e);
Expand Down Expand Up @@ -243,49 +286,74 @@ <h3>Spanish Letter</h3>

function applyLipogramFilter() {
if (!fileContent) {
alert('Please upload a text file first');
return;
}

if (selectedLetters.size === 0) {
alert('Please select at least one letter to filter');
resultsSection.style.display = 'none';
return;
}

// Split text into words while preserving some punctuation structure
const words = fileContent.split(/\s+/);
const totalWords = words.filter(w => w.trim().length > 0).length;

// Filter words that don't contain any selected letters
const filteredWords = words.filter(word => {
if (word.trim().length === 0) return false;

// Check if word contains any of the selected letters
for (let letter of selectedLetters) {
if (wordContainsLetter(word, letter)) {
return false;
let filteredWords;
if (filterMode === 'remove') {
// Filter words that don't contain any selected letters (original behavior)
filteredWords = words.filter(word => {
if (word.trim().length === 0) return false;

// Check if word contains any of the selected letters
for (let letter of selectedLetters) {
if (wordContainsLetter(word, letter)) {
return false;
}
}
}
return true;
});
return true;
});
} else {
// Keep only words that contain at least one selected letter (inverse mode)
filteredWords = words.filter(word => {
if (word.trim().length === 0) return false;

// Check if word contains any of the selected letters
for (let letter of selectedLetters) {
if (wordContainsLetter(word, letter)) {
return true;
}
}
return false;
});
}

const filteredText = filteredWords.join(' ');
const remainingWords = filteredWords.length;
const removedWords = totalWords - remainingWords;
const removalPercentage = ((removedWords / totalWords) * 100).toFixed(1);
const keptPercentage = ((remainingWords / totalWords) * 100).toFixed(1);

outputText.textContent = filteredText || 'No words remaining after filter.';

const selectedLettersList = Array.from(selectedLetters).join(', ');
const modeText = filterMode === 'remove' ? 'Removed' : 'Kept';
statsDisplay.innerHTML = `
<strong>Filtered Letters:</strong> ${selectedLettersList}<br>
<strong>Filter Mode:</strong> ${modeText} words ${filterMode === 'remove' ? 'WITH' : 'WITHOUT'} selected letters<br>
<strong>Selected Letters:</strong> ${selectedLettersList}<br>
<strong>Original Word Count:</strong> ${totalWords}<br>
<strong>Remaining Words:</strong> ${remainingWords}<br>
<strong>Remaining Words:</strong> ${remainingWords} (${keptPercentage}%)<br>
<strong>Removed Words:</strong> ${removedWords} (${removalPercentage}%)
`;

resultsSection.style.display = 'block';
resultsSection.scrollIntoView({ behavior: 'smooth' });
}

// Font size slider
const fontSizeSlider = document.getElementById('fontSizeSlider');
if (fontSizeSlider) {
fontSizeSlider.addEventListener('input', (e) => {
outputText.style.fontSize = e.target.value + 'px';
});
}

// Initialize on page load
Expand Down
Loading