/** * Returns element's start tag. * @return string */ public function startTag() { if (!$this->name) { return ''; } $s = '<' . $this->name; if (is_array($this->attrs)) { foreach ($this->attrs as $key => $value) { // skip NULLs and false boolean attributes if ($value === NULL || $value === FALSE) { continue; } // true boolean attribute if ($value === TRUE) { // in XHTML must use unminimized form if (self::$xhtml) { $s .= ' ' . $key . '="' . $key . '"'; } else { $s .= ' ' . $key; } continue; } elseif (is_array($value)) { // prepare into temporary array $tmp = NULL; foreach ($value as $k => $v) { // skip NULLs & empty string; composite 'style' vs. 'others' if ($v == NULL) { continue; } elseif (is_string($k)) { $tmp[] = $k . ':' . $v; } else { $tmp[] = $v; } } if (!$tmp) { continue; } $value = implode($key === 'style' ? ';' : ' ', $tmp); } else { $value = (string) $value; } // add new attribute $value = str_replace(array('&', '"', '<', '>', '@'), array('&', '"', '<', '>', '@'), $value); $s .= ' ' . $key . '="' . Texy::freezeSpaces($value) . '"'; } } // finish start tag if (self::$xhtml && $this->isEmpty) { return $s . ' />'; } return $s . '>'; }
private function cb($matches) { list(, $mText, $mComment, $mEnd, $mTag, $mAttr, $mEmpty) = $matches; $s = ''; if ($mText !== '') { $item = reset($this->tagStack); if ($item && !isset($item['dtdContent']['%DATA'])) { } elseif (!empty($this->tagUsed['pre']) || !empty($this->tagUsed['textarea']) || !empty($this->tagUsed['script'])) { $s = Texy::freezeSpaces($mText); } else { $s = preg_replace('#[ \\n]+#', ' ', $mText); } } if ($mComment) { return $s . '<' . Texy::freezeSpaces($mComment) . '>'; } $mEmpty = $mEmpty || isset(TexyHtml::$emptyElements[$mTag]); if ($mEmpty && $mEnd) { return $s; } if ($mEnd) { if (empty($this->tagUsed[$mTag])) { return $s; } $tmp = array(); $back = TRUE; foreach ($this->tagStack as $i => $item) { $tag = $item['tag']; $s .= $item['close']; $this->space -= $item['indent']; $this->tagUsed[$tag]--; $back = $back && isset(TexyHtml::$inlineElements[$tag]); unset($this->tagStack[$i]); if ($tag === $mTag) { break; } array_unshift($tmp, $item); } if (!$back || !$tmp) { return $s; } $item = reset($this->tagStack); if ($item) { $dtdContent = $item['dtdContent']; } else { $dtdContent = $this->baseDTD; } if (!isset($dtdContent[$tmp[0]['tag']])) { return $s; } foreach ($tmp as $item) { $s .= $item['open']; $this->space += $item['indent']; $this->tagUsed[$item['tag']]++; array_unshift($this->tagStack, $item); } } else { $dtdContent = $this->baseDTD; if (!isset($this->texy->dtd[$mTag])) { $allowed = TRUE; $item = reset($this->tagStack); if ($item) { $dtdContent = $item['dtdContent']; } } else { foreach ($this->tagStack as $i => $item) { $dtdContent = $item['dtdContent']; if (isset($dtdContent[$mTag])) { break; } $tag = $item['tag']; if ($item['close'] && (!isset(TexyHtml::$optionalEnds[$tag]) && !isset(TexyHtml::$inlineElements[$tag]))) { break; } $s .= $item['close']; $this->space -= $item['indent']; $this->tagUsed[$tag]--; unset($this->tagStack[$i]); $dtdContent = $this->baseDTD; } $allowed = isset($dtdContent[$mTag]); if ($allowed && isset(TexyHtml::$prohibits[$mTag])) { foreach (TexyHtml::$prohibits[$mTag] as $pTag) { if (!empty($this->tagUsed[$pTag])) { $allowed = FALSE; break; } } } } if ($mEmpty) { if (!$allowed) { return $s; } if ($this->xml) { $mAttr .= " /"; } $indent = $this->indent && empty($this->tagUsed['pre']) && empty($this->tagUsed['textarea']); if ($indent && $mTag === 'br') { return rtrim($s) . '<' . $mTag . $mAttr . ">\n" . str_repeat("\t", max(0, $this->space - 1)) . ""; } if ($indent && !isset(TexyHtml::$inlineElements[$mTag])) { $space = "\r" . str_repeat("\t", $this->space); return $s . $space . '<' . $mTag . $mAttr . '>' . $space; } return $s . '<' . $mTag . $mAttr . '>'; } $open = NULL; $close = NULL; $indent = 0; if ($allowed) { $open = '<' . $mTag . $mAttr . '>'; if (!empty($this->texy->dtd[$mTag][1])) { $dtdContent = $this->texy->dtd[$mTag][1]; } if ($this->indent && !isset(TexyHtml::$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; } } $item = array('tag' => $mTag, 'open' => $open, 'close' => $close, 'dtdContent' => $dtdContent, 'indent' => $indent); array_unshift($this->tagStack, $item); $tmp =& $this->tagUsed[$mTag]; $tmp++; } return $s; }
/** * Callback function: <tag> | </tag> | .... * @return string */ private 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); // text not allowed? if ($item && !isset($item['dtdContent']['%DATA'])) { } elseif (!empty($this->tagUsed['pre']) || !empty($this->tagUsed['textarea']) || !empty($this->tagUsed['script'])) { $s = Texy::freezeSpaces($mText); } else { $s = preg_replace('#[ \\n]+#', ' ', $mText); } } // phase #2 - HTML comment if ($mComment) { return $s . '<' . Texy::freezeSpaces($mComment) . '>'; } // phase #3 - HTML tag $mEmpty = $mEmpty || isset(TexyHtml::$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 = array(); $back = TRUE; foreach ($this->tagStack as $i => $item) { $tag = $item['tag']; $s .= $item['close']; $this->space -= $item['indent']; $this->tagUsed[$tag]--; $back = $back && isset(TexyHtml::$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); if ($item) { $dtdContent = $item['dtdContent']; } else { $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(TexyHtml::$optionalEnds[$tag]) && !isset(TexyHtml::$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(TexyHtml::$prohibits[$mTag])) { foreach (TexyHtml::$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 .= " /"; } if ($this->indent && $mTag === 'br') { // formatting exception return rtrim($s) . '<' . $mTag . $mAttr . ">\n" . str_repeat("\t", max(0, $this->space - 1)) . ""; } if ($this->indent && !isset(TexyHtml::$inlineElements[$mTag])) { $space = "\r" . str_repeat("\t", $this->space); return $s . $space . '<' . $mTag . $mAttr . '>' . $space; } return $s . '<' . $mTag . $mAttr . '>'; } $open = NULL; $close = NULL; $indent = 0; /* if (!isset(TexyHtml::$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(TexyHtml::$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 = array('tag' => $mTag, 'open' => $open, 'close' => $close, 'dtdContent' => $dtdContent, 'indent' => $indent); array_unshift($this->tagStack, $item); $tmp =& $this->tagUsed[$mTag]; $tmp++; } return $s; }