31 protected $_blocks =
'address article area aside blockquote caption col colgroup dd 32 details div dl dt fieldset figure figcaption footer form h1 h2 h3 h4 h5 h6 header 33 hr hgroup legend map math menu nav noscript p pre section select style summary 34 table tbody td tfoot th thead tr ul ol option li';
39 protected $_inlines =
'a abbr audio b button canvas caption cite code command datalist 40 del dfn em embed i iframe img input ins kbd keygen label map mark meter object 41 output progress q rp rt ruby s samp script select small source span strong style 42 sub sup textarea time var video wbr';
49 protected $_descendList =
'article aside blockquote body details div footer form 57 protected $_alterList =
'article aside blockquote body details div footer header 69 $this->_blocks = preg_split(
'@\\s+@', $this->_blocks);
70 $this->_descendList = preg_split(
'@\\s+@', $this->_descendList);
71 $this->_alterList = preg_split(
'@\\s+@', $this->_alterList);
72 $this->_inlines = preg_split(
'@\\s+@', $this->_inlines);
73 $this->_unique =
md5(__FILE__);
98 $this->_doc =
new DOMDocument();
102 $use_internal_errors = libxml_use_internal_errors(
true);
104 if (!$this->_doc->loadHTML(
"<html><meta http-equiv='content-type' " 105 .
"content='text/html; charset={$this->encoding}'><body>{$html}</body>" 106 .
"</html>", LIBXML_NOBLANKS)) {
107 libxml_use_internal_errors($use_internal_errors);
111 libxml_use_internal_errors($use_internal_errors);
113 $this->_xpath =
new DOMXPath($this->_doc);
116 $nodeList = $this->_xpath->query(
'//body[1]');
117 if ($nodeList->item(0) instanceof DOMText) {
120 $this->_xpath =
new DOMXPath($this->_doc);
121 $nodeList = $this->_xpath->query(
'//body[1]');
123 if ($nodeList->item(0) instanceof DOMText) {
125 throw new \RuntimeException(
'DOMXPath::query for BODY element returned a text node');
131 $html = $this->_doc->saveHTML();
136 $html = preg_replace(
'/(' . $this->_unique .
'NL){2,}/',
'</autop><autop>', $html);
137 $html =
str_replace([$this->_unique .
'BR', $this->_unique .
'NL',
'<br>'],
140 $html =
str_replace(
'<br /></autop>',
'</autop>', $html);
146 $use_internal_errors = libxml_use_internal_errors(
true);
148 if (!$this->_doc->loadHTML($html)) {
149 libxml_use_internal_errors($use_internal_errors);
153 libxml_use_internal_errors($use_internal_errors);
156 $this->_xpath =
new DOMXPath($this->_doc);
159 foreach ($this->_xpath->query(
'//autop') as $autop) {
162 if (
trim($autop->textContent) !==
'') {
165 foreach ($autop->childNodes as $node) {
166 if ($node->nodeType === XML_ELEMENT_NODE) {
174 $autop->setAttribute(
"r",
"1");
179 foreach ($this->_xpath->query(
'//div') as $el) {
181 $autops = $this->_xpath->query(
'./autop', $el);
182 if ($autops->length === 1) {
183 $firstAutop = $autops->item(0);
185 $firstAutop->setAttribute(
"r",
"1");
189 $html = $this->_doc->saveHTML();
193 $bodyEnd =
elgg_strpos($html,
'</body>', $bodyStart + 6);
194 $html =
elgg_substr($html, $bodyStart + 6, $bodyEnd - $bodyStart - 6);
197 $html = preg_replace(
'@<autop r="1">(.*?)</autop>@',
'\\1', $html);
204 $html =
str_replace($this->_unique .
'AMP',
'&', $html);
216 $elsToProcess = [$el];
217 $inlinesToProcess = [];
218 while ($el = array_shift($elsToProcess)) {
221 $alterInline = in_array($el->nodeName, $this->_alterList);
225 $ltrimFirstTextNode =
true;
232 $isFollowingBr =
false;
234 $node = $el->firstChild;
235 while (null !== $node) {
240 $autop = $el->insertBefore($this->_doc->createElement(
'autop'), $node);
244 $isElement = ($node->nodeType === XML_ELEMENT_NODE);
246 $isBlock = in_array($node->nodeName, $this->_blocks);
249 $ltrimFirstTextNode =
false;
256 $isText = ($node->nodeType === XML_TEXT_NODE);
257 $isLastInline = (! $node->nextSibling
258 || ($node->nextSibling->nodeType === XML_ELEMENT_NODE
259 && in_array($node->nextSibling->nodeName, $this->_blocks)));
261 $isFollowingBr = ($node->nodeName ===
'br');
265 $nodeText = $node->nodeValue;
267 if ($ltrimFirstTextNode) {
269 $nodeText = ltrim($nodeText);
270 $ltrimFirstTextNode =
false;
272 if ($isFollowingBr &&
preg_match(
'@^[ \\t]*\\n[ \\t]*@', $nodeText, $m)) {
278 $nodeText =
rtrim($nodeText);
280 $nodeText =
str_replace(
"\n", $this->_unique .
'NL', $nodeText);
282 $node = $node->nextSibling;
285 $tmpNode->nodeValue = $nodeText;
286 $autop->appendChild($tmpNode);
291 if ($isBlock || ! $node->nextSibling) {
293 if (in_array($node->nodeName, $this->_descendList)) {
294 $elsToProcess[] = $node;
299 $ltrimFirstTextNode =
true;
304 if ($isElement &&
false !==
elgg_strpos($tmpNode->textContent,
"\n")) {
305 $inlinesToProcess[] = $tmpNode;
307 $node = $node->nextSibling;
308 $autop->appendChild($tmpNode);
313 $node = $node->nextSibling;
319 while ($el = array_shift($inlinesToProcess)) {
320 $ignoreLeadingNewline =
false;
321 foreach ($el->childNodes as $node) {
322 if ($node->nodeType === XML_ELEMENT_NODE) {
323 if ($node->nodeValue ===
'BR') {
324 $ignoreLeadingNewline =
true;
326 $ignoreLeadingNewline =
false;
327 if (
false !==
elgg_strpos($node->textContent,
"\n")) {
328 $inlinesToProcess[] = $node;
332 }
elseif ($node->nodeType === XML_TEXT_NODE) {
333 $text = $node->nodeValue;
334 if (
$text[0] ===
"\n" && $ignoreLeadingNewline) {
336 $ignoreLeadingNewline =
false;
process($html)
Create wrapper P and BR elements in HTML depending on newlines.
elgg_strlen()
Wrapper function for mb_strlen().
elgg_strpos()
Wrapper function for mb_strpos().
elgg_substr()
Wrapper function for mb_substr().
if($item instanceof\ElggEntity) elseif($item instanceof\ElggRiverItem) elseif($item instanceof ElggRelationship) elseif(is_callable([$item, 'getType']))
addParagraphs(DOMElement $el)
Add P and BR elements as necessary.
Create wrapper P and BR elements in HTML depending on newlines.
__construct()
Constructor.