1<?php 2/** 3 * @copyright Copyright (c) 2014 Carsten Brandt 4 * @license https://github.com/cebe/markdown/blob/master/LICENSE 5 * @link https://github.com/cebe/markdown#readme 6 */ 7 8namespace cebe\markdown\block; 9 10/** 11 * Adds inline and block HTML support 12 */ 13trait HtmlTrait 14{ 15 /** 16 * @var array HTML elements considered as inline elements. 17 * @see http://www.w3.org/wiki/HTML/Elements#Text-level_semantics 18 */ 19 protected $inlineHtmlElements = [ 20 'a', 'abbr', 'acronym', 21 'b', 'basefont', 'bdo', 'big', 'br', 'button', 'blink', 22 'cite', 'code', 23 'del', 'dfn', 24 'em', 25 'font', 26 'i', 'img', 'ins', 'input', 'iframe', 27 'kbd', 28 'label', 'listing', 29 'map', 'mark', 30 'nobr', 31 'object', 32 'q', 33 'rp', 'rt', 'ruby', 34 's', 'samp', 'script', 'select', 'small', 'spacer', 'span', 'strong', 'sub', 'sup', 35 'tt', 'var', 36 'u', 37 'wbr', 38 'time', 39 ]; 40 /** 41 * @var array HTML elements known to be self-closing. 42 */ 43 protected $selfClosingHtmlElements = [ 44 'br', 'hr', 'img', 'input', 'nobr', 45 ]; 46 47 /** 48 * identify a line as the beginning of a HTML block. 49 */ 50 protected function identifyHtml($line, $lines, $current) 51 { 52 if ($line[0] !== '<' || isset($line[1]) && $line[1] == ' ') { 53 return false; // no html tag 54 } 55 56 if (strncmp($line, '<!--', 4) === 0) { 57 return true; // a html comment 58 } 59 60 $gtPos = strpos($lines[$current], '>'); 61 $spacePos = strpos($lines[$current], ' '); 62 if ($gtPos === false && $spacePos === false) { 63 return false; // no html tag 64 } elseif ($spacePos === false) { 65 $tag = rtrim(substr($line, 1, $gtPos - 1), '/'); 66 } else { 67 $tag = rtrim(substr($line, 1, min($gtPos, $spacePos) - 1), '/'); 68 } 69 70 if (!ctype_alnum($tag) || in_array(strtolower($tag), $this->inlineHtmlElements)) { 71 return false; // no html tag or inline html tag 72 } 73 return true; 74 } 75 76 /** 77 * Consume lines for an HTML block 78 */ 79 protected function consumeHtml($lines, $current) 80 { 81 $content = []; 82 if (strncmp($lines[$current], '<!--', 4) === 0) { // html comment 83 for ($i = $current, $count = count($lines); $i < $count; $i++) { 84 $line = $lines[$i]; 85 $content[] = $line; 86 if (strpos($line, '-->') !== false) { 87 break; 88 } 89 } 90 } else { 91 $tag = rtrim(substr($lines[$current], 1, min(strpos($lines[$current], '>'), strpos($lines[$current] . ' ', ' ')) - 1), '/'); 92 $level = 0; 93 if (in_array($tag, $this->selfClosingHtmlElements)) { 94 $level--; 95 } 96 for ($i = $current, $count = count($lines); $i < $count; $i++) { 97 $line = $lines[$i]; 98 $content[] = $line; 99 $level += substr_count($line, "<$tag") - substr_count($line, "</$tag>") - substr_count($line, "/>"); 100 if ($level <= 0) { 101 break; 102 } 103 } 104 } 105 $block = [ 106 'html', 107 'content' => implode("\n", $content), 108 ]; 109 return [$block, $i]; 110 } 111 112 /** 113 * Renders an HTML block 114 */ 115 protected function renderHtml($block) 116 { 117 return $block['content'] . "\n"; 118 } 119 120 /** 121 * Parses an & or a html entity definition. 122 * @marker & 123 */ 124 protected function parseEntity($text) 125 { 126 // html entities e.g. © © © 127 if (preg_match('/^&#?[\w\d]+;/', $text, $matches)) { 128 return [['inlineHtml', $matches[0]], strlen($matches[0])]; 129 } else { 130 return [['text', '&'], 1]; 131 } 132 } 133 134 /** 135 * renders a html entity. 136 */ 137 protected function renderInlineHtml($block) 138 { 139 return $block[1]; 140 } 141 142 /** 143 * Parses inline HTML. 144 * @marker < 145 */ 146 protected function parseInlineHtml($text) 147 { 148 if (strpos($text, '>') !== false) { 149 if (preg_match('~^</?(\w+\d?)( .*?)?>~s', $text, $matches)) { 150 // HTML tags 151 return [['inlineHtml', $matches[0]], strlen($matches[0])]; 152 } elseif (preg_match('~^<!--.*?-->~s', $text, $matches)) { 153 // HTML comments 154 return [['inlineHtml', $matches[0]], strlen($matches[0])]; 155 } 156 } 157 return [['text', '<'], 1]; 158 } 159 160 /** 161 * Escapes `>` characters. 162 * @marker > 163 */ 164 protected function parseGt($text) 165 { 166 return [['text', '>'], 1]; 167 } 168} 169