public static function autop($value) { $value = str_replace("\r\n", "\n", $value); $value = trim($value); $ht = new HtmlTokenizer($value); $set = $ht->parse(); $value = ''; // should never autop ANY content in these items $noAutoP = array( 'pre','blockquote','code','ul' ); $blockElements = array( 'address','blockquote','center','dir','div','dl','fieldset','form', 'h1','h2','h3','h4','h5','h6','hr','isindex','menu','noframes', 'noscript','ol','p','pre','table','ul' ); $token = $set->current(); $openP = false; do { if ($openP) { if ($token['type'] == HTMLTokenizer::NODE_TYPE_ELEMENT_OPEN && in_array(strtolower($token['name']), $blockElements)) { $value .= '

'; $openP = false; } } // no-autop, pass them through verbatim if ($token['type'] == HTMLTokenizer::NODE_TYPE_ELEMENT_OPEN && in_array(strtolower($token['name']), $noAutoP)) { $nestedToken = $token; $count = 0; do { $value .= HtmlTokenSet::token_to_string($nestedToken); if ( ($nestedToken['type'] == HTMLTokenizer::NODE_TYPE_ELEMENT_CLOSE && strtolower($nestedToken['name']) == strtolower($token['name'])) // found closing element ) { break; } } while ($nestedToken = $set->next()); continue; } // anything that's not a text node should get passed through if ($token['type'] != HTMLTokenizer::NODE_TYPE_TEXT) { $value .= HtmlTokenSet::token_to_string($token); continue; } // if we get this far, token type is text $localValue = $token['value']; if (strlen($localValue)) { // contains newlines: if ($openP) { $localValue = $localValue; } else { $localValue = "

{$localValue}"; $openP = true; } $localValue = preg_replace('/\s*(\n\s*){2,}/', "

", $localValue); // at least two \n in a row (allow whitespace in between) $localValue = str_replace("\n", "
\n", $localValue); // nl2br $localValue = str_replace('

', "

\n", $localValue); // and replace the line break for legibility } $value .= $localValue; } while ($token = $set->next()); $value = preg_replace('!\s*

\s*!', "\n", $value); // replace

if ($openP) { $value .= '

'; } return $value; }