/** * Callback function: <tag> | </tag> | .... * @return string * @internal */ public function cb($matches) { // html tag list(, $mText, $mComment, $mEnd, $mTag, $mAttr, $mEmpty) = $matches; // [1] => text // [1] => !-- comment -- // [2] => / // [3] => TAG // [4] => ... (attributes) // [5] => / (empty) $s = ''; // phase #1 - stuff between tags if ($mText !== '') { $item = reset($this->tagStack); if ($item && !isset($item['dtdContent']['%DATA'])) { // text not allowed? } elseif (array_intersect(array_keys($this->tagUsed, TRUE), $this->preserveSpaces)) { // inside pre & textarea preserve spaces $s = Texy\Helpers::freezeSpaces($mText); } else { $s = Regexp::replace($mText, '#[ \\n]+#', ' '); // otherwise shrink multiple spaces } } // phase #2 - HTML comment if ($mComment) { return $s . '<' . Texy\Helpers::freezeSpaces($mComment) . '>'; } // phase #3 - HTML tag $mEmpty = $mEmpty || isset(HtmlElement::$emptyElements[$mTag]); if ($mEmpty && $mEnd) { return $s; // bad tag; /end/ } if ($mEnd) { // end tag // has start tag? if (empty($this->tagUsed[$mTag])) { return $s; } // autoclose tags $tmp = []; $back = TRUE; foreach ($this->tagStack as $i => $item) { $tag = $item['tag']; $s .= $item['close']; $this->space -= $item['indent']; $this->tagUsed[$tag]--; $back = $back && isset(HtmlElement::$inlineElements[$tag]); unset($this->tagStack[$i]); if ($tag === $mTag) { break; } array_unshift($tmp, $item); } if (!$back || !$tmp) { return $s; } // allowed-check (nejspis neni ani potreba) $item = reset($this->tagStack); $dtdContent = $item ? $item['dtdContent'] : $this->baseDTD; if (!isset($dtdContent[$tmp[0]['tag']])) { return $s; } // autoopen tags foreach ($tmp as $item) { $s .= $item['open']; $this->space += $item['indent']; $this->tagUsed[$item['tag']]++; array_unshift($this->tagStack, $item); } } else { // start tag $dtdContent = $this->baseDTD; if (!isset($this->texy->dtd[$mTag])) { // unknown (non-html) tag $allowed = TRUE; $item = reset($this->tagStack); if ($item) { $dtdContent = $item['dtdContent']; } } else { // optional end tag closing foreach ($this->tagStack as $i => $item) { // is tag allowed here? $dtdContent = $item['dtdContent']; if (isset($dtdContent[$mTag])) { break; } $tag = $item['tag']; // auto-close hidden, optional and inline tags if ($item['close'] && (!isset(HtmlElement::$optionalEnds[$tag]) && !isset(HtmlElement::$inlineElements[$tag]))) { break; } // close it $s .= $item['close']; $this->space -= $item['indent']; $this->tagUsed[$tag]--; unset($this->tagStack[$i]); $dtdContent = $this->baseDTD; } // is tag allowed in this content? $allowed = isset($dtdContent[$mTag]); // check deep element prohibitions if ($allowed && isset(HtmlElement::$prohibits[$mTag])) { foreach (HtmlElement::$prohibits[$mTag] as $pTag) { if (!empty($this->tagUsed[$pTag])) { $allowed = FALSE; break; } } } } // empty elements se neukladaji do zasobniku if ($mEmpty) { if (!$allowed) { return $s; } if ($this->xml) { $mAttr .= ' /'; } $indent = $this->indent && !array_intersect(array_keys($this->tagUsed, TRUE), $this->preserveSpaces); if ($indent && $mTag === 'br') { // formatting exception return rtrim($s) . '<' . $mTag . $mAttr . ">\n" . str_repeat("\t", max(0, $this->space - 1)) . ""; } elseif ($indent && !isset(HtmlElement::$inlineElements[$mTag])) { $space = "\r" . str_repeat("\t", $this->space); return $s . $space . '<' . $mTag . $mAttr . '>' . $space; } else { return $s . '<' . $mTag . $mAttr . '>'; } } $open = NULL; $close = NULL; $indent = 0; /* if (!isset(Texy\HtmlElement::$inlineElements[$mTag])) { // block tags always decorate with \n $s .= "\n"; $close = "\n"; } */ if ($allowed) { $open = '<' . $mTag . $mAttr . '>'; // receive new content (ins & del are special cases) if (!empty($this->texy->dtd[$mTag][1])) { $dtdContent = $this->texy->dtd[$mTag][1]; } // format output if ($this->indent && !isset(HtmlElement::$inlineElements[$mTag])) { $close = "" . '</' . $mTag . '>' . "\n" . str_repeat("\t", $this->space); $s .= "\n" . str_repeat("\t", $this->space++) . $open . ""; $indent = 1; } else { $close = '</' . $mTag . '>'; $s .= $open; } // TODO: problematic formatting of select / options, object / params } // open tag, put to stack, increase counter $item = ['tag' => $mTag, 'open' => $open, 'close' => $close, 'dtdContent' => $dtdContent, 'indent' => $indent]; array_unshift($this->tagStack, $item); $tmp =& $this->tagUsed[$mTag]; $tmp++; } return $s; }