Question

i write something like [spoiler=Spoiler Title]text inside the Spoiler[/spoiler] and use preg_replace_callback("/\[spoiler=(.*)\](.*)\[\/spoiler\]/Usi", 'BBCode_spoiler', $text); to create a real spoiler, the result with one or more spoiler is:

<script type="text/javascript">
    function show_0() {
        if(document.getElementById("0").style.display == "inline-block")
            document.getElementById("0").style.display = "none";
        else document.getElementById("0").style.display = "inline-block"; }
</script>
<a href="javascript:show_0();"><i>Show Spiler:</i> <b>Spoiler Title</a></b><br>
<div id="0" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
    Text inside the Spoiler
</div>

how do i make it works with a spoiler into a spoiler like [spoiler=Spoiler Title][spoiler=Second Spoiler]Another Text[/spoiler][/spoiler] my current function returns by none spoiler into a spoiler

<script type="text/javascript">
    function show_0() {
        if(document.getElementById("0").style.display == "inline-block")
            document.getElementById("0").style.display = "none";
        else document.getElementById("0").style.display = "inline-block"; }
</script>
<a href="javascript:show_0();"><i>Show Spiler:</i> <b>Spoiler Title</a></b><br>
<div id="0" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
    [spoiler=Second&nbsp;Spoiler]Another Text</div>[/spoiler]

my callback function is

<?php
// Spoiler
$counter = 0;
function BBCode_spoiler($hits) {
    global $central_lang;
    global $counter;
    $title = htmlentities(trim($hits[1]));
    $text = htmlentities($hits[2]);
    $return = "<script type=\"text/javascript\">";
    $return .= "function show_".$counter."() {";
    $return .= "if(document.getElementById(\"".$counter."\").style.display == \"inline-block\") document.getElementById(\"".$counter."\").style.display = \"none\";";
    $return .= "else document.getElementById(\"".$counter."\").style.display = \"inline-block\"; }";
    $return .= "</script>";
    $return .= "<a href=\"javascript:show_".$counter."();\"><i>".$central_lang['bbcodes']['spoiler']['text'].":</i> <b>".$title."</a></b><br>";
    $return .= "<div id=\"".$counter."\" style=\"display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;\">".$text."</div>";
    $counter++;
    return $return; }
?>

the output i try to make seems like that

<script type="text/javascript">
    function show_0() {
        if(document.getElementById("0").style.display == "inline-block")
            document.getElementById("0").style.display = "none";
        else document.getElementById("0").style.display = "inline-block"; }
</script>
<a href="javascript:show_0();"><i>Show Spoiler:</i> <b>Spoiler Title</a></b><br>
<div id="0" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
    <script type="text/javascript">
        function show_1() {
            if(document.getElementById("1").style.display == "inline-block")
                document.getElementById("1").style.display = "none";
            else document.getElementById("1").style.display = "inline-block"; }
    </script>
    <a href="javascript:show_1();"><i>Show Spoiler:</i> <b>Second Spoiler</a></b><br>
    <div id="1" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
        Another text
    </div>
</div>

i hope it's there is an answer for my question, thank you!

Was it helpful?

Solution

This applies to BBCode in general: re-run the replacement code until it stops changing it.

preg_replace_callback accepts a "count" reference variable, which will be filled with the number of replacements made. So long as this number is not zero, you should re-run the replacement (do..while is perfect for this)

It doesn't matter that they're crossed over. Let's say we have a BBCode that replaces [div] with <div>...

[div]Blah[div]123[/div]Fish[/div]

After one replacement:

<div>Blah[div]123</div>Fish[/div]

After another replacement:

<div>Blah<div>123</div>Fish</div>

So even though they were processed in a crossed-over order, the result is properly nested.

OTHER TIPS

You can use the count parameter of preg_replace_callback to process the replacement from innermost to outermost until there is no more tags to replace:

$pattern = '~\[spoiler=([^]]*)]((?>[^[]+|\[(?!/?spoiler\b))*)\[/spoiler]~i';

do {
    $result = preg_replace_callback($pattern, 'BBCode_spoiler', $text, -1, $count);
} while ($count>0);

I change a little the pattern to ensure that the match is the innermost tag.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top