init2
This commit is contained in:
parent
4e3e22b175
commit
d8f3fc991d
324
index.html
324
index.html
@ -340,11 +340,11 @@
|
||||
<h2>Charger un dictionnaire</h2>
|
||||
<div class="form-group">
|
||||
<input type="text" id="dictionaryUrl" placeholder="Ou entrez une URL pour charger un dictionnaire (raw .txt)" style="width:100%; padding:8px 12px; border:1px solid #d1d5db; border-radius:4px; margin-bottom:8px;">
|
||||
<div style="margin-top:8px;margin-bottom:8px;"><button class="btn-primary" onclick="loadDictionaryFromUrl()">Charger depuis URL</button></div>
|
||||
<textarea id="dictionaryText" rows="5" placeholder="Collez le contenu du dictionnaire ici (format: mot: définition)"></textarea>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px;">
|
||||
<button class="btn-success" onclick="loadDictionaryFromTextarea()">Charger depuis zone</button>
|
||||
<button class="btn-primary" onclick="loadDictionaryFromUrl()">Charger depuis URL</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -473,37 +473,36 @@
|
||||
|
||||
if (cell.type === 'definition') {
|
||||
// Determine associated words in both directions (starting at the neighboring letter cell)
|
||||
const wordInfoH = getWordAt(row, col + 1, 'horizontal');
|
||||
const wordInfoV = getWordAt(row + 1, col, 'vertical');
|
||||
// Special rules for top row and left column mappings
|
||||
let wordInfoH;
|
||||
let wordInfoV;
|
||||
|
||||
let targetWord = null;
|
||||
let targetDir = null;
|
||||
|
||||
if (half === 'h') {
|
||||
targetWord = wordInfoH;
|
||||
targetDir = 'horizontal';
|
||||
} else if (half === 'v') {
|
||||
targetWord = wordInfoV;
|
||||
targetDir = 'vertical';
|
||||
// Horizontal definition normally maps to word starting at (row, col+1) horizontally,
|
||||
// but if this definition is on the top row, its horizontal definition maps to the vertical word starting at (row, col+1).
|
||||
if (row === 0) {
|
||||
wordInfoH = getWordAt(row, col + 1, 'vertical');
|
||||
} else {
|
||||
// if no half specified, pick the direction that actually has a word
|
||||
if (wordInfoH && wordInfoH.word) {
|
||||
targetWord = wordInfoH;
|
||||
targetDir = 'horizontal';
|
||||
} else if (wordInfoV && wordInfoV.word) {
|
||||
targetWord = wordInfoV;
|
||||
targetDir = 'vertical';
|
||||
}
|
||||
wordInfoH = getWordAt(row, col + 1, 'horizontal');
|
||||
}
|
||||
|
||||
if (targetWord && targetWord.word) {
|
||||
const normalized = normalizeWord(targetWord.word);
|
||||
const defs = dictionaries[normalized] || [];
|
||||
const uniqueDefs = [...new Set(defs)];
|
||||
|
||||
currentModal = { row, col, half, direction: targetDir };
|
||||
showDefinitions(targetWord.word, uniqueDefs);
|
||||
// Vertical definition normally maps to word starting at (row+1, col) vertically,
|
||||
// but if this definition is on the first column, its vertical definition maps to the horizontal word starting at (row+1, col).
|
||||
if (col === 0) {
|
||||
wordInfoV = getWordAt(row + 1, col, 'horizontal');
|
||||
} else {
|
||||
wordInfoV = getWordAt(row + 1, col, 'vertical');
|
||||
}
|
||||
|
||||
// Build words and definitions for both sides (may be empty)
|
||||
const wordH = (wordInfoH && wordInfoH.word) ? wordInfoH.word : null;
|
||||
const wordV = (wordInfoV && wordInfoV.word) ? wordInfoV.word : null;
|
||||
|
||||
const defsH = wordH ? ([...new Set(dictionaries[normalizeWord(wordH)] || [])]) : [];
|
||||
const defsV = wordV ? ([...new Set(dictionaries[normalizeWord(wordV)] || [])]) : [];
|
||||
|
||||
// If half specified, still show modal but prioritize that half for selection via UI
|
||||
currentModal = { row, col, half };
|
||||
showDefinitionModal(row, col, wordH, defsH, wordV, defsV);
|
||||
} else {
|
||||
if (activeCell && activeCell.row === row && activeCell.col === col) {
|
||||
orientation = orientation === 'horizontal' ? 'vertical' : 'horizontal';
|
||||
@ -527,44 +526,110 @@
|
||||
renderGrid();
|
||||
}
|
||||
|
||||
// Affiche les définitions
|
||||
function showDefinitions(word, definitions) {
|
||||
document.getElementById('modalTitle').textContent = `Définitions pour "${word}"`;
|
||||
// Affiche le modal de sélection des définitions, en deux parties (horizontal / vertical)
|
||||
function showDefinitionModal(row, col, wordH, defsH, wordV, defsV) {
|
||||
currentModal = { row, col };
|
||||
document.getElementById('modalTitle').textContent = `Définitions`;
|
||||
const list = document.getElementById('definitionList');
|
||||
list.innerHTML = '';
|
||||
|
||||
if (definitions.length === 0) {
|
||||
list.innerHTML = '<p style="color: #6b7280;">Aucune définition disponible pour ce mot.</p>';
|
||||
const sectionH = document.createElement('div');
|
||||
const titleH = document.createElement('h4');
|
||||
titleH.textContent = 'Mot horizontal';
|
||||
sectionH.appendChild(titleH);
|
||||
if (!wordH) {
|
||||
const p = document.createElement('p');
|
||||
p.style.color = '#6b7280';
|
||||
p.textContent = 'Aucun mot associé';
|
||||
sectionH.appendChild(p);
|
||||
} else {
|
||||
definitions.forEach(def => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'definition-item';
|
||||
btn.textContent = def;
|
||||
btn.onclick = () => selectDefinition(def);
|
||||
list.appendChild(btn);
|
||||
});
|
||||
const wordP = document.createElement('p');
|
||||
wordP.style.fontWeight = '600';
|
||||
wordP.textContent = wordH;
|
||||
sectionH.appendChild(wordP);
|
||||
|
||||
if (defsH.length === 0) {
|
||||
const p = document.createElement('p');
|
||||
p.style.color = '#6b7280';
|
||||
p.textContent = 'Aucune définition disponible pour ce mot.';
|
||||
sectionH.appendChild(p);
|
||||
} else {
|
||||
defsH.forEach(def => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'definition-item';
|
||||
btn.textContent = def;
|
||||
btn.onclick = () => {
|
||||
grid[row][col].definitionH = def;
|
||||
closeModal();
|
||||
renderGrid();
|
||||
};
|
||||
sectionH.appendChild(btn);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const sectionV = document.createElement('div');
|
||||
const titleV = document.createElement('h4');
|
||||
titleV.textContent = 'Mot vertical';
|
||||
sectionV.appendChild(titleV);
|
||||
if (!wordV) {
|
||||
const p = document.createElement('p');
|
||||
p.style.color = '#6b7280';
|
||||
p.textContent = 'Aucun mot associé';
|
||||
sectionV.appendChild(p);
|
||||
} else {
|
||||
const wordP = document.createElement('p');
|
||||
wordP.style.fontWeight = '600';
|
||||
wordP.textContent = wordV;
|
||||
sectionV.appendChild(wordP);
|
||||
|
||||
if (defsV.length === 0) {
|
||||
const p = document.createElement('p');
|
||||
p.style.color = '#6b7280';
|
||||
p.textContent = 'Aucune définition disponible pour ce mot.';
|
||||
sectionV.appendChild(p);
|
||||
} else {
|
||||
defsV.forEach(def => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'definition-item';
|
||||
btn.textContent = def;
|
||||
btn.onclick = () => {
|
||||
grid[row][col].definitionV = def;
|
||||
closeModal();
|
||||
renderGrid();
|
||||
};
|
||||
sectionV.appendChild(btn);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Delete button: transforms case into letter empty
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.className = 'btn-danger';
|
||||
deleteBtn.style.marginTop = '12px';
|
||||
deleteBtn.textContent = 'Supprimer (transformer en case lettre)';
|
||||
deleteBtn.onclick = () => {
|
||||
const cell = grid[row][col];
|
||||
const hasDefs = !!(cell.definitionH || cell.definitionV);
|
||||
if (hasDefs) {
|
||||
if (!confirm('La case contient des définitions. Confirmer la transformation en case lettre vide ?')) return;
|
||||
}
|
||||
cell.type = 'letter';
|
||||
cell.letter = '';
|
||||
cell.definitionH = '';
|
||||
cell.definitionV = '';
|
||||
closeModal();
|
||||
renderGrid();
|
||||
};
|
||||
|
||||
// Append sections to list
|
||||
list.appendChild(sectionH);
|
||||
list.appendChild(sectionV);
|
||||
list.appendChild(deleteBtn);
|
||||
|
||||
document.getElementById('definitionModal').classList.add('show');
|
||||
}
|
||||
|
||||
// Sélectionne une définition
|
||||
function selectDefinition(definition) {
|
||||
if (!currentModal) return;
|
||||
|
||||
const { row, col, direction } = currentModal;
|
||||
|
||||
if (direction === 'horizontal') {
|
||||
grid[row][col].definitionH = definition;
|
||||
} else {
|
||||
grid[row][col].definitionV = definition;
|
||||
}
|
||||
|
||||
closeModal();
|
||||
renderGrid();
|
||||
}
|
||||
|
||||
// Ferme le modal
|
||||
function closeModal() {
|
||||
document.getElementById('definitionModal').classList.remove('show');
|
||||
currentModal = null;
|
||||
@ -763,47 +828,36 @@
|
||||
} else {
|
||||
hasBoth = cell.definitionH && cell.definitionV;
|
||||
|
||||
if (hasBoth) {
|
||||
const split = document.createElement('div');
|
||||
split.className = 'definition-split';
|
||||
if (hasBoth) {
|
||||
const split = document.createElement('div');
|
||||
split.className = 'definition-split';
|
||||
|
||||
const halfH = document.createElement('div');
|
||||
halfH.className = 'definition-half';
|
||||
halfH.textContent = cell.definitionH;
|
||||
halfH.innerHTML += '<svg class="arrow" width="8" height="8" viewBox="0 0 8 8"><path d="M 2 4 L 6 4 M 6 4 L 4 2 M 6 4 L 4 6" stroke="black" stroke-width="1" fill="none"/></svg>';
|
||||
const halfH = document.createElement('div');
|
||||
halfH.className = 'definition-half';
|
||||
halfH.textContent = cell.definitionH;
|
||||
|
||||
const halfV = document.createElement('div');
|
||||
halfV.className = 'definition-half';
|
||||
halfV.textContent = cell.definitionV;
|
||||
halfV.innerHTML += '<svg class="arrow" width="8" height="8" viewBox="0 0 8 8"><path d="M 4 2 L 4 6 M 4 6 L 2 4 M 4 6 L 6 4" stroke="black" stroke-width="1" fill="none"/></svg>';
|
||||
const halfV = document.createElement('div');
|
||||
halfV.className = 'definition-half';
|
||||
halfV.textContent = cell.definitionV;
|
||||
|
||||
split.appendChild(halfH);
|
||||
split.appendChild(halfV);
|
||||
cellEl.appendChild(split);
|
||||
split.appendChild(halfH);
|
||||
split.appendChild(halfV);
|
||||
cellEl.appendChild(split);
|
||||
|
||||
halfH.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
handleCellClick(rowIndex, colIndex, 'h');
|
||||
};
|
||||
halfV.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
handleCellClick(rowIndex, colIndex, 'v');
|
||||
};
|
||||
} else {
|
||||
const text = document.createElement('div');
|
||||
text.className = 'definition-text';
|
||||
text.textContent = cell.definitionH || cell.definitionV;
|
||||
|
||||
if (cell.definitionH || cell.definitionV) {
|
||||
if (cell.definitionV) {
|
||||
text.innerHTML += '<svg class="arrow" width="8" height="8" viewBox="0 0 8 8"><path d="M 4 2 L 4 6 M 4 6 L 2 4 M 4 6 L 6 4" stroke="black" stroke-width="1" fill="none"/></svg>';
|
||||
halfH.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
handleCellClick(rowIndex, colIndex, 'h');
|
||||
};
|
||||
halfV.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
handleCellClick(rowIndex, colIndex, 'v');
|
||||
};
|
||||
} else {
|
||||
text.innerHTML += '<svg class="arrow" width="8" height="8" viewBox="0 0 8 8"><path d="M 2 4 L 6 4 M 6 4 L 4 2 M 6 4 L 4 6" stroke="black" stroke-width="1" fill="none"/></svg>';
|
||||
const text = document.createElement('div');
|
||||
text.className = 'definition-text';
|
||||
text.textContent = cell.definitionH || cell.definitionV;
|
||||
cellEl.appendChild(text);
|
||||
}
|
||||
}
|
||||
|
||||
cellEl.appendChild(text);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasBoth) {
|
||||
@ -812,6 +866,10 @@
|
||||
cellEl.ondblclick = () => handleCellDoubleClick(rowIndex, colIndex);
|
||||
|
||||
rowEl.appendChild(cellEl);
|
||||
// store reference for later arrow placement
|
||||
if (!window._cellEls) window._cellEls = [];
|
||||
if (!window._cellEls[rowIndex]) window._cellEls[rowIndex] = [];
|
||||
window._cellEls[rowIndex][colIndex] = cellEl;
|
||||
});
|
||||
|
||||
// Bouton d'ajout de colonne
|
||||
@ -833,6 +891,90 @@
|
||||
addRowLine.appendChild(addBtn);
|
||||
}
|
||||
gridEl.appendChild(addRowLine);
|
||||
|
||||
// After grid built, place arrows in the first-letter cells based on adjacent definition cells
|
||||
// Clear any previous arrows
|
||||
if (window._cellEls) {
|
||||
for (let r = 0; r < grid.length; r++) {
|
||||
for (let c = 0; c < grid[0].length; c++) {
|
||||
const el = window._cellEls[r] && window._cellEls[r][c];
|
||||
if (el) {
|
||||
const prev = el.querySelector('.def-arrow');
|
||||
if (prev) prev.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each definition cell, compute target first-letter cell and append arrow there
|
||||
for (let dr = 0; dr < grid.length; dr++) {
|
||||
for (let dc = 0; dc < grid[0].length; dc++) {
|
||||
const defCell = grid[dr][dc];
|
||||
if (defCell.type !== 'definition') continue;
|
||||
|
||||
// Horizontal-definition mapping
|
||||
if (defCell.definitionH) {
|
||||
const tr = dr;
|
||||
const tc = dc + 1;
|
||||
if (tr >= 0 && tr < grid.length && tc >= 0 && tc < grid[0].length) {
|
||||
const targetEl = window._cellEls[tr] && window._cellEls[tr][tc];
|
||||
if (targetEl) {
|
||||
const arrow = document.createElement('div');
|
||||
arrow.className = 'def-arrow';
|
||||
// special top-row: curve down
|
||||
if (dr === 0) {
|
||||
arrow.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1" class="arrow-down-curved"><path d="M12 0 C12 0 12 6 12 6 L12 16"/><path d="M12 16 L9 13 M12 16 L15 13"/></svg>';
|
||||
arrow.style.position = 'absolute';
|
||||
arrow.style.top = '2px';
|
||||
arrow.style.left = '50%';
|
||||
arrow.style.transform = 'translateX(-50%)';
|
||||
} else {
|
||||
// left-pointing arrow near left border
|
||||
arrow.innerHTML = '<svg width="12" height="12" viewBox="0 0 8 8" fill="none" stroke="black" stroke-width="1"><path d="M6 1 L2 4 L6 7"/></svg>';
|
||||
arrow.style.position = 'absolute';
|
||||
arrow.style.left = '4px';
|
||||
arrow.style.top = '50%';
|
||||
arrow.style.transform = 'translateY(-50%)';
|
||||
}
|
||||
arrow.style.pointerEvents = 'none';
|
||||
arrow.classList.add('def-arrow');
|
||||
targetEl.appendChild(arrow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical-definition mapping
|
||||
if (defCell.definitionV) {
|
||||
const tr = dr + 1;
|
||||
const tc = dc;
|
||||
if (tr >= 0 && tr < grid.length && tc >= 0 && tc < grid[0].length) {
|
||||
const targetEl = window._cellEls[tr] && window._cellEls[tr][tc];
|
||||
if (targetEl) {
|
||||
const arrow = document.createElement('div');
|
||||
arrow.className = 'def-arrow';
|
||||
if (dc === 0) {
|
||||
// left-edge special: curve right
|
||||
arrow.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1"><path d="M0 12 C0 12 6 12 6 12 L16 12"/><path d="M16 12 L13 9 M16 12 L13 15"/></svg>';
|
||||
arrow.style.position = 'absolute';
|
||||
arrow.style.left = '2px';
|
||||
arrow.style.top = '50%';
|
||||
arrow.style.transform = 'translateY(-50%)';
|
||||
} else {
|
||||
// up-pointing arrow near top border
|
||||
arrow.innerHTML = '<svg width="12" height="12" viewBox="0 0 8 8" fill="none" stroke="black" stroke-width="1"><path d="M1 6 L4 2 L7 6"/></svg>';
|
||||
arrow.style.position = 'absolute';
|
||||
arrow.style.top = '4px';
|
||||
arrow.style.left = '50%';
|
||||
arrow.style.transform = 'translateX(-50%)';
|
||||
}
|
||||
arrow.style.pointerEvents = 'none';
|
||||
arrow.classList.add('def-arrow');
|
||||
targetEl.appendChild(arrow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion du clavier
|
||||
|
||||
Loading…
Reference in New Issue
Block a user