Autor Zpráva
Fisir
Profil
Ahoj.
Mám text formátovaný pomocí BB kódů. Ty postupně parsuji, mohou být jak řádkové ([i], [b], …) tak i blokové ([li], [h], …). Potud je to OK. Ale nemám tagy pro ohraničení odstavců. (A ani je nechci přidávat, snižuje to komfort.) Jsem ve stavu, kdy mám všechy BB kódy zpracované a mám vyrobené HTML.

Nyní však potřebuji samostatné bloky textu přeměnit na odstavce (uzavřít je do elementu p). A tady je problém. Jednoduchý regulární výraz typu nahraď všechna dvojitá odřádkování za </p><p> použít nemůžu, protože se v kódu vyskytují právě třeba ty seznamy (<li>).

Pokoušel jsem se toho dosáhnout použitím třeba HTMLPurifieru, ale to je kanón na vrabce, protože šíleně zpomalí načítání a já z něj potřebuji právě jen tu jednu funkci. Pak jsem zkusil jakousi funkci, která mi ovšem do odstavce obalila i konstrukce typu <h2>Bla<a name="bla>#bla</a></h2>.

Nevíte o nějakém řešení, knihovně, …?
Str4wberry
Profil
Menší kanón na vrabce je Texy!.

Jinak přesně proč vadí odstavec v položce seznamu?
Fisir
Profil
Reaguji na Str4wberryho:
Jinak přesně proč vadí odstavec v položce seznamu?
Asi jsem se špatně vyjádřil. Ze druhé zmíněné varianty vylezlo toto:
<p><ul></p>
<li>položka seznamu</li>
<p></ul></p>
Jinak odstavec v položce seznamu mi nevadí.
Jan Tvrdík
Profil
Fisir:
Buď si můžeš postavit AST z bbcode, nebo DOM z výsledného HTML a nad tím pak pustit inteligentní algoritmus na vytváření těch odstavců.
Joker
Profil
Primitivnější, ale možná účinná metoda by byla nejdřív nahradit ty ostatní značky a požrat odřádkování a potom udělat odstavce.

Koukám, jak se ten problém řeší v RS na Péhápku. A jestli koukám dobře, prostě ty „přebytečné“ odstavce následně odstraní.

Jinak při nahrazování BBCode bude asi lepší do kódu vkládat jen <p> než </p><p>, ukončovací značka odstavce není povinná a ten výraz s ní bude do kódu vkládat nadbytečné ukončovací značky (protože ne vždycky je v tu chvíli nějaký odstavec vůbec otevřený).
Jan Tvrdík
Profil
Joker:
Koukám, jak se ten problém řeší v RS na Péhápku
Na to moc nekoukej, protože zrovna tam je to vyřešené špatně.
Fisir
Profil
Děkuji.

Poradíte mi, jak se dají v DOMDocumentu najít takové čistě textové nebo inline nody?
Jan Tvrdík
Profil
Fisir:
stackoverflow.com/questions/5033955/xpath-select-text-node
Fisir
Profil
Dobře. Mám tohle:
$textnodes = $xpath->query('//text()');
foreach($textnodes as $textnode){
    if($textnode->parentNode->nodeName === 'body' and !empty($textnode->nodeValue)){
        $lines = explode("\n", $textnode->nodeValue);
        $replacement = array();
        foreach($lines as $line){
            $replacement = $dom->createElement('p', rtrim($line, "\n\r"));
            $textnode->parentNode->insertBefore($replacement, $textnode);
        }
        $textnode->parentNode->removeChild($textnode);
    }
}

Což mi zajistí, že se mi samostatné bloky textu obalí do odstavce. Teď se mi ale nedaří zajistit, aby se do odstavce obalovaly i inline elementy. Nemůžu najít žádný selektor, který by mi vybral inline elementy.

Tak nic, tohle je taky blbost. Nějak to nefunguje na všem.
Jan Tvrdík
Profil
Fisir:
odstavce obalovaly i inline elementy
Čemu říkáš inline elementy? Ty, co mají ve výchozím display: inline? Jako třeba <img>?

Tak nic, tohle je taky blbost. Nějak to nefunguje na všem.
Neměl bys konkrétní příklad, kdy to nefunguje? Já bych řekl, že na to jdeš docela dobře, ač to samozřejmě není dotažené.


Co něco jako

function createParagraphs($html)
{
    $whitelist = [
        'a', 'b', 'body', 'del', 'div', 'em', 'h1', 'h2', 'h3',
        'h4', 'h5', 'h6', 'i', 'ins', 'small', 'span', 'strong',
    ];

    $document = new DOMDocument();
    $document->loadHTML($html);

    $xpath = new DOMXPath($document);
    foreach ($xpath->query('//text()') as $textNode) {
        $parent = $textNode->parentNode;
        if (in_array($parent->nodeName, $whitelist, TRUE)) {
            $lines = explode("\n", $textNode->nodeValue);
            foreach ($lines as $line) {
                $line = rtrim($line);
                if ($line !== '') {
                    $p = $document->createElement('p', $line);
                    $parent->insertBefore($p, $textNode);
                }
            }
            $parent->removeChild($textNode);
        }
    }

    return $document->saveHTML();
}
Fisir
Profil
Reaguji na Jana Tvrdíka:
Použil jsem tvoji funkci, jako vstup jsem ji dal:
<h2>Kapitola 1</h2>
Tohle je <strong>jenom</strong> testovací <em>text</em>.

Nic tu není ke čtení.

Další text…

<strong>Vážně ne.</strong>

<pre>Zato je tu <span class="css-property">nějaký</span>
kód.

Kód, kód.</pre>

Výsledek:
<h2><p>Kapitola 1</p></h2>
<p>Tohle je</p>
<strong><p>jenom</p></strong><p> testovac&iacute;</p>
<em><p>text</p></em><p>.</p>
<p>Nic tu nen&iacute; ke čten&iacute;.</p>
<p>Dal&scaron;&iacute; text&hellip;</p>
<strong><p>V&aacute;žně ne.</p></strong><pre>Zato je tu <span class="css-property"><p>nějak&yacute;</p></span>
k&oacute;d.

K&oacute;d, k&oacute;d.</pre>

Jestliže jsem použil tu svou, vyšlo toto:
<h2>Kapitola 1</h2>
<p></p>
<p>Tohle je </p>
<strong>jenom</strong><p> testovac&iacute; </p>
<em>text</em><p>.</p>
<p></p>
<p>Nic tu nen&iacute; ke čten&iacute;.</p>
<p></p>
<p>Dal&scaron;&iacute; text&hellip;</p>
<p></p>
<p></p>
<strong>V&aacute;žně ne.</strong><p></p>
<p></p>
<p></p>
<pre>Zato je tu <span class="css-property">nějak&yacute;</span>
k&oacute;d.

K&oacute;d, k&oacute;d.</pre>
(Prázdné odstavce bych uměl odstranit.)

Optimální výsledek by měl být:
<h2>Kapitola 1</h2>
<p>Tohle je <strong>jenom</strong> testovací <em>text</em>.</p>
<p>Nic tu není ke čtení.</p>
<p>Další text…</p>
<p><strong>Vážně ne.</strong></p>
<pre>Zato je tu <span class="css-property">nějaký</span>
kód.

Kód, kód.</pre>

Čemu říkáš inline elementy? Ty, co mají ve výchozím display: inline? Jako třeba <img>?
Ano, přesně ty.

Momentálně bych tedy potřeboval zajistit, aby to „ignorovalo“ inline elementy – neukončilo to při jejich přítomnosti odstavec ale zahrnulo je do něj.
Fisir
Profil
Vyřešil jsem další problém s vytvářením odstavců. V případě, že vstup byl pouze a jen text (žádné další elementy), automaticky se obalil do odstavce. Ale celý, tudíž ho potom funkce nechytala. Obalil jsem tedy vstup do div#container a nyní kontroluji, zda je právě on rodič. Když ano, rozsekám obsah text nodu po řádkách a obalím je do odstavce.

Při použití mojí původní funkce (která se nakonec jeví jako lepší, je s ní méně potíží) už odstraňuji prázdné elementy.

Problém s rozdělením odstavce u inline elementů stále přetrvává.
Jan Tvrdík
Profil
Fisir:
To mi připomíná, že jsem to řešení sice ve čtvrtek napsal, ale zapomněl jsem ho sem poslat =)

function createParagraphs(DOMElement $body)
{
    $inlineTags = ['a', 'b', 'del', 'em', 'i', 'img', 'ins', 'small', 'span', 'strong'];
    $create = TRUE;
    $document = $body->ownerDocument;
    for ($node = $body->firstChild; $node; $node = $next) {
        $next = $node->nextSibling;

        if ($node instanceof DOMText) {
            $children = explode("\n", $node->nodeValue);
            $body->removeChild($node);
            $node = $next;

        } elseif ($node instanceof DOMElement && in_array($node->nodeName, $inlineTags, TRUE)) {
            $children = [$node];

        } else {
            $children = [];
            $create = TRUE;
        }

        foreach ($children as $child) {
            if ($child !== '') {
                if ($create) {
                    $p = $body->insertBefore($document->createElement('p'), $node);
                    $create = FALSE;
                }
                $p->appendChild(is_string($child) ? $document->createTextNode($child) : $child);

            } else {
                $create = TRUE;
            }
        }
    }
}
Fisir
Profil
Reaguji na Jana Tvrdíka:
Jo, díky, já už jsem to vyřešil tak, že jsem nejdřív převedl blokové BB kódy, vyrobil odstavce a až pak proběhl převod těch řádkových.
Jan Tvrdík
Profil
Fisir:
To nebude fungovat moc spolehlivě, ne? Třeba v následujícím případě to udělá odstavec uvnitř <i>.

[i]
A

B
[/i]
Fisir
Profil
Reaguji na Jana Tvrdíka:
Hm, ani já si nejsem příliš jistý, jak by se v tomto případě měl parser zachovat, ale myslím, že takovéhle konstrukce to snad ani nepotká. Každopádně, ta tvá funkce funguje nějakým (pro mne) záhadným způsobem, protože odstavce zastavuje až na začátku dalšího blokového elementu a někdy je rozdělí i u řádkového. Asi to nemá cenu dál řešit.

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: