So, I spent some time with that plugin and improved it to "does simply work". I didn't count the hours ...
Basically, i left the nl2br untouched and upgraded the nl2p version to something which is universally usable. Means: if you write simple text, it formats the article with paragraphs, depending on your newlines. That did it before ... more or less. But now, it also can digest any html input, and that according to the specifications. So, out of:
Code: Select all
this is some text
next line
<div>
<ol>
<li>first point
<li>second point
no paragraphs allowed in ol
</ol>
</div>
some text
with break
it generates this output:
Code: Select all
<p_class="wl_bottom">this is some text</p>
<p_class="wl_bottom">next line</p>
<div>
<ol>
<li>first point
<li>second point
no paragraphs allowed in ol
</ol>
</div>
<p class="wl_top">some text</p>
<p class="break">with break</p>
The CSS would be accordingly with margin on top,bottom, both or none(=break).
If you want to test it, i put the core into a short demonstration of the html coding:
http://s521456705.online.de/webspace/testnl2br.php
I also rejuvenated the isolation tag feature, this was not working with nl2p (contrary to the description), in the demonstrator it is <nl>, in the plugin it gets the tags from the config.
This extended version makes paragraphs only where they are allowed, simply put. You can even make the Tags in Uppercase, or insert white spaces ... It is open to discussion if it should automatically delete the last nl in the article / extended article or push that newline from article to extended, but the core is the formatting of plain text seasoned with html tags.
I've not put it into a second Pull Request yet, because it's too late and I have to tidy up the code a bit.
So, it does what it should better than before, it extends the functionality of something existing without breaking the previous functionality and i'm really tired if nobody appreciates that. Because there is simply no reason why it shouldn't be integrated in the core and that happened too often to me in the past, so that I even wrote a post about that:
http://www.intjblog.de/index.php?/archi ... Hilfe.html
Oh, and because the plugin now splits the text into content and html tags and keeps a stack of the active tags, it wouldn't be that much work to automatically fix overlappings, like a <p> inside a <em>. According to the specs, the paragraph closes the inline element, and now it is easily possible to close the inline element before and open it again on the new paragraph. Not implemented yet, but the structure now allows that. If somebody wants that. I think that not everyone wants to use a full-fledged WYSIWYG-Editor and the standard interface uses html tags and you need simply
something to make your paragraphs, because I don't want to write all the <p> tags by hand.
But the preliminary version is like this:
Code: Select all
// following w3.org, these elements close p elements automatically:
var $block_elements = array('table','ul','ol','pre', 'dl',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'menu', 'section',
'address', 'article', 'aside', 'fieldset', 'footer',
'form', 'header', 'hgroup', 'hr', 'main', 'nav', 'p'
);
var $nested_block_elements = array('div','table','blockquote');
/* paragraphs aren't allowed in these inline elements -> p closes these elements:
* var $inline_elements = array('b', 'big', 'i', 'small', 'tt', 'abbr',
'acronym', 'cite', 'code', 'dfn', 'em', 'kbd', 'strong',
'samp', 'var', 'a', 'bdo', 'br', 'map', 'object',
'q', 'script', 'span', 'sub', 'sup', 'button',
'label', 'select', 'textarea'
); */
var $allowed_p_parents = array('blockquote', 'td', 'div', 'article', 'aside', 'dd',
'details', 'dl', 'dt', 'footer', 'header', 'summary'
);
/** Make sure that all the tags are in lowercase
* @param string text
* @return text
*/
function tag_tolower($textstring)
{
$text = str_split($textstring);
$tag = false;
for ($i = 0; $i < count($text); $i++)
{
if ($text[$i] == '<' ) { $tag = true; }
elseif ($tag && ($text[$i] == ' ' || $text[$i] == '>') )
{ $tag = false; }
elseif ($tag)
{ $text[$i] = strtolower($text[$i]); }
}
return implode($text);
}
/**
* remove spaces in end tags
* @param string text
* @return text
*/
function trim_end_tags($textstring)
{
$text = str_split($textstring);
$endtag = false;
for ($i = 1; $i < count($text); $i++)
{
if ($text[$i] == '/' && $text[$i-1] == '<' )
{ $endtag = true; }
elseif ($endtag && $text[$i] == ' ' )
{ $text[$i] = ''; }
elseif ($endtag && $text[$i] == '>')
{ $endtag = false; }
}
return implode($text);
}
/**
* Insert <p class="whiteline" at paragraphs ending with two newlines
* Insert <p class="break" at paragraphs ending with one or no nl
* Insert <p class="whitelinetop" at the first paragraph if starting with a nl
* Insert <p class="whitelinetopbottom" if the first paragraph is ending with two newlines
* @param string text
* @param boolean complex operations (not necessary when text is flat)
* @return string
*/
function nl2p($textstring)
{
//check for empty content
if (empty(trim($textstring))) { return $textstring; }
//check for whiteline on start and end
$startnl = ( strspn($textstring,"\n") ) ? true : false;
$endnl = ( strspn($textstring,"\n",-1 ) ) ? true : false;
$whiteline = $endnl;
//trim whitespaces and line breaks
$textstring = trim($textstring);
if (empty($textstring)) { return $textstring; }
$p_top = '<p class="wl_top">';
$p_bottom = '<p_class="wl_bottom">';
$p_top_bottom = '<p_class="wl_top_bottom">';
$p = '<p class="break">';
$end_p = '</p>';
// check for newlines in text block
if ($startnl || $endnl || strpos($textstring,"\n") )
{
//split into array
$text = str_split($textstring);
$i = count($text) - 1;
//main operation: convert \n to p, dependent of the context
//crawling from end to start
while ($i > 0) {
//search next /n enclosing text, starting at $i-1
$i = $this->next_nl_block($i, $text);
if ($i == 0) { //no newlines left
break;
} elseif ($whiteline) {
$text[$i] = "</p>" . $p_bottom;
} else {
$text[$i] = "</p>" . $p;
}
//look ahead for next paragraph class
if ($text[$i-1] == "\n") {
$whiteline = true;
$text[$i-1] = '';
$i--;
} else {
$whiteline = false;
}
}
//implode
$textstring = implode($text);
//get type of first paragraph
if ($whiteline && $startnl) {
$textstring = $p_top_bottom . $textstring . $end_p;
} elseif ($startnl) {
$textstring = $p_top . $textstring . $end_p;
} elseif ($whiteline) {
$textstring = $p_bottom . $textstring . $end_p;
} else {
$textstring = $p . $textstring . $end_p;
}
//format output
if ($startnl) { $textstring = "\n" . $textstring; }
$textstring = str_replace('</p>',"</p>\n",$textstring);
}
return $textstring;
}
/**
* Find next newline separated by text from current position
* @param int start
* $param array text
*/
function next_nl_block($i, $text)
{
$skipped = false;
for ($i--; $i>0; $i-- ) {
if (!$skipped){
//see if you skipped over a non-newline (heading to the next block)
if (strpos($text[$i], "\n") === false) {
$skipped = true;
}
} else if (strpos($text[$i], "\n") !== false) {
break;
}
}
return $i;
}
/** explode textstring into array of substrings
* array element can be tag or content
* @param text
* $return array of tags and contents
*/
function explode_along_tags($text)
{
$startpos = 0;
$endpos = 0;
$textarray = array();
do
{
//find tag start
$endpos = strpos($text,'<',$startpos);
if ($endpos === false)
{
//no more tags, copy remainder to array
$endpos = strlen($text);
if ($endpos - $startpos > 0)
{ $textarray[] = substr($text,$startpos,$endpos - $startpos); }
return $textarray;
}
elseif (($endpos - $startpos) > 0)
{
//copy preliminary text to array
$textarray[] = substr($text,$startpos,$endpos - $startpos);
}
$startpos = $endpos;
//find tag end
$endpos = strpos($text,'>',$startpos);
if ($endpos === false) { return false; }
elseif (($endpos - $startpos) > 1)
{
//copy tag to array
$textarray[] = substr($text,$startpos,$endpos - $startpos + 1);
$startpos = $endpos + 1;
}
else { return false; }
} while (1);
return false;
}
function extract_tag($text)
{
if ($text[0] != "<") { return false; }
$n = strcspn($text," >");
return ltrim(substr($text,0,$n),"</");
}
function is_starttag($text) { return ($text[1] == "/") ? false : true; }
/*
* handles tagged content, apply nl2p if tag allows it
* works also for ommitted closing tags
* @param: text
* return string
*/
function tagged_nl2p($text)
{
//join config isolation tags into block elements
//if (!empty($isolate)) { $block_elements = array_merge($block_elements,$isolate); }
//explode string into array of tags and contents
$textarray = $this->explode_along_tags($text);
$content = "";
$start = 0;
$tagstack = array();
$isolation_flag = false;
for ($i=0; $i < count($textarray); $i++)
{
//delete isolation tags from other arrays
if ($this->isolationtags)
{
$this->block_elements = array_diff($this->block_elements,$this->isolationtags);
$this->allowed_p_parents = array_diff($this->allowed_p_parents,$this->isolationtags);
$this->nested_block_elements = array_diff($this->nested_block_elements,$this->isolationtags);
}
//get tag or false if none
$tag = $this->extract_tag($textarray[$i]);
//new blocktag - e.g. <table>
if ($tag && $this->is_starttag($textarray[$i])
&& (in_array($tag, $this->block_elements) || in_array($tag, $this->nested_block_elements) ))
{
//merge previous content, apply nl2p if needed and concatenate
if (!$isolation_flag && empty($tagstack) )
{
$content .= $this->nl2p(implode(array_slice($textarray,$start,$i-$start)));
} elseif (!$isolation_flag && in_array($tagstack[0], $this->allowed_p_parents) )
{
$content .= $textarray[$start]
. $this->nl2p(implode(array_slice($textarray,$start+1,$i-$start-1)));
} else
{
$content .= implode(array_slice($textarray,$start,$i-$start));
}
// clear stack of block elements and insert
if (in_array($tag, $this->block_elements) )
{
$tagstack = array_diff($tagstack, $this->block_elements);
}
array_unshift($tagstack, $tag);
$start = $i;
}
//new tag which can contain paragraphs and can be inside a blocktag - e.g. <td>
elseif ($tag && $this->is_starttag($textarray[$i]) && in_array($tag, $this->allowed_p_parents))
{
//merge previous content, apply nl2p if needed and concatenate
if (!$isolation_flag && empty($tagstack) )
{
$content .= $this->nl2p(implode(array_slice($textarray,$start,$i-$start)));
} elseif (!$isolation_flag && in_array($tagstack[0], $this->allowed_p_parents) )
{
$content .= $textarray[$start]
. $this->nl2p(implode(array_slice($textarray,$start+1,$i-$start-1)));
} else
{
$content .= implode(array_slice($textarray,$start,$i-$start));
}
//insert tag into the stack
array_unshift($tagstack, $tag);
$start = $i;
}
//isolation tag
elseif($tag && $this->is_starttag($textarray[$i]) && in_array($tag, $this->isolationtags) )
{
//merge previous content, apply nl2p if needed and concatenate
if (!$isolation_flag && empty($tagstack) )
{
$content .= $this->nl2p(implode(array_slice($textarray,$start,$i-$start)));
} elseif (!$isolation_flag && in_array($tagstack[0], $this->allowed_p_parents) )
{
$content .= $textarray[$start]
. $this->nl2p(implode(array_slice($textarray,$start+1,$i-$start-1)));
} else
{
$content .= implode(array_slice($textarray,$start,$i-$start));
}
$isolation_flag = true;
$start = $i+1;
}
//closing isolation tag
elseif($tag && !$this->is_starttag($textarray[$i]) && in_array($tag, $this->isolationtags) )
{
if ($isolation_flag)
{
//content, no nl2p
$content .= implode(array_slice($textarray,$start,$i-$start-1));
$isolation_flag = false;
$start = $i+1;
}
}
//closing blocktag or p parent - e.g. </table> or </td>
elseif($tag && !$this->is_starttag($textarray[$i]) && $tag == $tagstack[0])
{
//concatenate the start tag
$content .= $textarray[$start];
//content, apply nl2p if needed
if (!$isolation_flag && in_array($tagstack[0], $this->allowed_p_parents) )
{
$content .= $this->nl2p(implode(array_slice($textarray,$start+1,$i-$start-1)));
} else
{
$content .= implode(array_slice($textarray,$start+1,$i-$start-1));
}
//closing tag
if ($i != $start) { $content .= $textarray[$i]; }
$start = $i+1;
array_shift($tagstack);
}
}
//merge remainder
if (!$isolation_flag && empty($tagstack) )
{
$content .= $this->nl2p(implode(array_slice($textarray,$start,$i-$start)));
} elseif (!$isolation_flag && in_array($tagstack[0], $this->allowed_p_parents) )
{
$content .= $textarray[$start]
. $this->nl2p(implode(array_slice($textarray,$start,$i-$start-1)));
} else
{
$content .= implode(array_slice($textarray,$start,$i-$start-1));
}
return $content;
}