Skip to content

Instantly share code, notes, and snippets.

@me-suzy
Created August 6, 2026 05:50
Show Gist options
  • Select an option

  • Save me-suzy/58301a52338882223f8963eba63337e9 to your computer and use it in GitHub Desktop.

Select an option

Save me-suzy/58301a52338882223f8963eba63337e9 to your computer and use it in GitHub Desktop.
rezolvare problema.txt
Problema
Format Painter și butoanele de aliniere nu mergeau constant fiindcă uneori textul din pagină nu mai era într-un paragraf real (`<p>`), ci rămânea ca text/span direct în `.page`, mai ales după paste sau editări. Atunci `getBlock()` / `blocksInSelection()` nu găseau corect paragraful, deci Justify sau Format Painter nu aveau o țintă bună.
Rezolvarea ta
Ai introdus normalizarea structurii înainte de comenzi:
`ensureBlockStructure()` caută “orfani” în pagină: text direct sau elemente inline direct în `.page`. Dacă le găsește, le înfășoară automat într-un `<p>`, păstrând selecția prin offset-uri de caractere.
Apoi ai legat asta în punctele importante:
- `blocksInSelection()` cheamă `ensureBlockStructure()` înainte să caute paragrafe.
- `toggleFormatPainter()` cheamă `ensureBlockStructure()` înainte să copieze stilul.
- `applyPainter()` cheamă `ensureBlockStructure()` înainte să aplice stilul pe țintă.
- după paste, chemi iar `ensureBlockStructure()`, fiindcă `execCommand('insertHTML')` poate lăsa text/span direct în coală.
A doua problemă
Format Painter lua uneori stilul greșit dacă dădeai click pe un cuvânt bold/italic dintr-un paragraf normal. Copia stilul cuvântului, nu stilul dominant al paragrafului.
Rezolvarea ta
Ai adăugat `dominantStyleElement(block)`: parcurge textele din paragraf, grupează stilurile și alege stilul care acoperă cele mai multe caractere. Așa Format Painter copiază stilul real al paragrafului, nu o excepție locală.
A treia problemă
Când aplicai Format Painter peste un paragraf deja formatat, se adăugau straturi noi de `<span>`, iar unele stiluri vechi rămâneau active.
Rezolvarea ta
Ai făcut Format Painter să înlocuiască formatarea, nu doar să adauge peste ea:
- `stripCharFormatting(block)` elimină/curăță tagurile de caracter (`strong`, `em`, `u`, `span`, `font` etc.).
- apoi aplică stilul copiat pe textul curat.
- alinierea rămâne pe bloc prin `applyPainterBlockStyle()`.
Pe scurt: soluția corectă nu era să forțezi “ultimul click”, ci să repari structura HTML a documentului înainte de comenzi și să copiezi/aplici stilul de paragraf real.
Da. Nu am modificat nimic. Părțile importante sunt în [index.php](</D:/Teste cursor/HTML Editor/Word Editor/index.php:3165>).
1. Problema: text/span orfan în pagină, fără `<p>`
Rezolvarea ta: înainte de aliniere / Format Painter, repari structura și bagi textul inline direct din `.page` într-un paragraf real.
```js
const BLOCK_TAG_RE = /^(P|H[1-6]|LI|UL|OL|BLOCKQUOTE|PRE|DIV|TABLE|HR|FIGURE)$/;
function isBlockNode(n) {
return n.nodeType === 1 && BLOCK_TAG_RE.test(n.tagName);
}
function isStrayInline(n) {
return (n.nodeType === 3 && n.nodeValue.trim() !== '') ||
(n.nodeType === 1 && !BLOCK_TAG_RE.test(n.tagName));
}
function normalizeSheetStrays() {
let changed = false;
page.querySelectorAll('.page').forEach(sh => {
let n = sh.firstChild;
while (n) {
if (!isStrayInline(n)) { n = n.nextSibling; continue; }
const p = document.createElement('p');
sh.insertBefore(p, n);
let m = p.nextSibling;
while (m && !isBlockNode(m)) {
const next = m.nextSibling;
p.appendChild(m);
m = next;
}
changed = true;
n = p.nextSibling;
}
});
return changed;
}
```
2. Problema: normalizarea strica poziția cursorului
Rezolvarea ta: salvezi selecția prin offset-uri de caractere, normalizezi, apoi restaurezi selecția.
```js
function ensureBlockStructure() {
if (!hasSheetStrays()) return false;
const s = selOffset('start'), e = selOffset('end');
if (!normalizeSheetStrays()) return false;
if (s != null && setSelectionByOffsets(s, e)) saveEditorSelection();
return true;
}
```
3. Problema: alinierea nu găsea corect paragraful
Rezolvarea ta: `blocksInSelection()` repară întâi structura, abia apoi caută blocurile/paragrafele.
```js
function blocksInSelection() {
ensureBlockStructure(); // repară paragrafele „orfane" înainte de a le căuta
const sel = window.getSelection();
if (!sel.rangeCount) {
const b = getBlock();
return b ? [b] : [];
}
return blocksInRange(sel.getRangeAt(0));
}
```
4. Problema: Format Painter copia stilul cuvântului, nu al paragrafului
Rezolvarea ta: ai creat `dominantStyleElement()`, care ia stilul dominant din paragraf.
```js
function dominantStyleElement(block) {
if (!block) return null;
const totals = new Map();
const w = document.createTreeWalker(block, NodeFilter.SHOW_TEXT);
let tn;
while ((tn = w.nextNode())) {
const len = (tn.nodeValue || '').replace(/\s+/g, '').length;
if (!len) continue;
const el = tn.parentElement;
if (!el) continue;
const cs = getComputedStyle(el);
const key = [
cs.fontFamily,
cs.fontSize,
cs.color,
cs.fontWeight,
cs.fontStyle,
cs.textDecorationLine,
cs.backgroundColor
].join('|');
const t = totals.get(key) || { el, len: 0 };
t.len += len;
totals.set(key, t);
}
let best = null;
totals.forEach(t => { if (!best || t.len > best.len) best = t; });
return best ? best.el : block;
}
```
5. Format Painter: copierea corectă
Aici ai pus `ensureBlockStructure()` înainte de copiere și ai luat stilul dominant al paragrafului.
```js
function toggleFormatPainter() {
if (painter) { disarmPainter(); toast('Format Painter oprit'); return; }
restoreEditorSelection();
ensureBlockStructure();
const sel = window.getSelection();
let n = sel.rangeCount ? sel.getRangeAt(0).startContainer : null;
if (n && n.nodeType === 3) n = n.parentNode;
if (!n || !page.contains(n)) {
toast('Pune cursorul/selectează în textul-sursă, apoi click pe Format');
return;
}
const sourceBlock = blockOf(n);
const cs = getComputedStyle(dominantStyleElement(sourceBlock) || n);
const bcs = sourceBlock ? getComputedStyle(sourceBlock) : null;
painter = {
fontFamily: cs.fontFamily,
fontSize: cs.fontSize,
color: cs.color,
fontWeight: cs.fontWeight,
fontStyle: cs.fontStyle,
textDecoration: cs.textDecorationLine,
background: cs.backgroundColor,
textAlign: normalizeTextAlign(bcs ? bcs.textAlign : '')
};
document.getElementById('btnPainter').classList.add('active');
}
```
6. Problema: Format Painter adăuga span peste span
Rezolvarea ta: înainte să aplici stilul nou pe paragraf, cureți formatarea veche.
```js
const CHAR_FORMAT_TAGS = /^(B|STRONG|I|EM|U|S|STRIKE|FONT|SPAN)$/;
function stripCharFormatting(block) {
if (!block) return;
[...block.querySelectorAll('*')].forEach(el => {
if (!CHAR_FORMAT_TAGS.test(el.tagName)) return;
if (el.classList.contains('dex-bad') || el.hasAttribute('data-page-caret-marker')) return;
if (el.tagName !== 'SPAN') {
unwrapElement(el);
return;
}
FONT_PROPS.forEach(p => el.style.removeProperty(p));
el.style.removeProperty('text-decoration-line');
el.className = (el.className || '').replace(/\bst-[\w-]+/g, '').trim();
if (!el.className) el.removeAttribute('class');
if (!el.getAttribute('style')) el.removeAttribute('style');
if (!el.attributes.length) unwrapElement(el);
});
block.normalize();
}
```
7. Format Painter: aplicarea corectă pe paragraf
```js
function applyPainter(selectionOnly) {
if (!painter) return false;
const sel = window.getSelection();
if (!sel.rangeCount || !page.contains(sel.getRangeAt(0).startContainer)) {
disarmPainter();
return false;
}
ensureBlockStructure();
let range = sel.getRangeAt(0);
const useSel = selectionOnly && !range.collapsed && sel.toString().trim().length > 0;
let targetBlocks = [];
if (!useSel) {
const blk = getBlock();
if (!blk) {
disarmPainter();
toast('Click în interiorul unui paragraf');
return false;
}
targetBlocks = [blk];
range = document.createRange();
range.selectNodeContents(blk);
}
recordUndo();
targetBlocks.forEach(applyPainterBlockStyle);
if (!useSel) {
targetBlocks.forEach(b => {
stripCharFormatting(b);
FONT_PROPS.forEach(p => b.style.removeProperty(p));
});
range = document.createRange();
range.selectNodeContents(targetBlocks[0]);
}
wrapRangeTextNodes(range);
}
```
8. Problema la paste: textul nou pierdea stilul paragrafului
Rezolvarea ta: la paste iei formatul destinației, creezi `<p>` cu `text-align`, pui textul în `<span>` cu fontul corect, apoi normalizezi iar.
```js
function destinationFormat() {
const blk = getBlock();
if (!blk) return { align: '', css: '' };
const align = normalizeTextAlign(getComputedStyle(blk).textAlign);
const styled = dominantStyleElement(blk);
let css = '';
if (styled && styled !== blk) {
const cs = getComputedStyle(styled);
css = 'font-family:' + cs.fontFamily +
';font-size:' + cs.fontSize +
';color:' + cs.color +
';font-weight:' + cs.fontWeight +
';font-style:' + cs.fontStyle + ';';
}
return { align, css };
}
```
Pe scurt: ai rezolvat-o corect prin repararea HTML-ului intern al editorului, nu prin forțarea selecției. Asta era cheia.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment