PluginProbe
OPcache Manager / 3.3.0
OPcache Manager v3.3.0
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.2.0 1.3.0 1.3.1 1.3.2 2.0.0 2.1.0 2.10.0 2.11.0 2.12.0 2.13.0 2.13.1 2.14.0 2.2.0 2.3.0 2.3.1 2.3.2 2.4.0 2.5.0 2.6.0 All 36 releases
opcache-manager / includes / libraries / markdown / block / ListTrait.php

ListTrait.php in OPcache Manager 3.3.0, at includes/libraries/markdown/block/ListTrait.php

203 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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
8 namespace cebe\markdownparser\block;
9
10 /**
11 * Adds the list blocks
12 */
13 trait ListTrait
14 {
15 /**
16 * @var bool enable support `start` attribute of ordered lists. This means that lists
17 * will start with the number you actually type in markdown and not the HTML generated one.
18 * Defaults to `false` which means that numeration of all ordered lists(<ol>) starts with 1.
19 */
20 public $keepListStartNumber = false;
21
22 /**
23 * identify a line as the beginning of an ordered list.
24 */
25 protected function identifyOl($line)
26 {
27 return (($l = $line[0]) > '0' && $l <= '9' || $l === ' ') && preg_match('/^ {0,3}\d+\.[ \t]/', $line);
28 }
29
30 /**
31 * identify a line as the beginning of an unordered list.
32 */
33 protected function identifyUl($line)
34 {
35 $l = $line[0];
36 return ($l === '-' || $l === '+' || $l === '*') && (isset($line[1]) && (($l1 = $line[1]) === ' ' || $l1 === "\t")) ||
37 ($l === ' ' && preg_match('/^ {0,3}[\-\+\*][ \t]/', $line));
38 }
39
40 /**
41 * Consume lines for an ordered list
42 */
43 protected function consumeOl($lines, $current)
44 {
45 // consume until newline
46
47 $block = [
48 'list',
49 'list' => 'ol',
50 'attr' => [],
51 'items' => [],
52 ];
53 return $this->consumeList($lines, $current, $block, 'ol');
54 }
55
56 /**
57 * Consume lines for an unordered list
58 */
59 protected function consumeUl($lines, $current)
60 {
61 // consume until newline
62
63 $block = [
64 'list',
65 'list' => 'ul',
66 'items' => [],
67 ];
68 return $this->consumeList($lines, $current, $block, 'ul');
69 }
70
71 private function consumeList($lines, $current, $block, $type)
72 {
73 $item = 0;
74 $indent = '';
75 $len = 0;
76 $lastLineEmpty = false;
77 // track the indentation of list markers, if indented more than previous element
78 // a list marker is considered to be long to a lower level
79 $leadSpace = 3;
80 $marker = $type === 'ul' ? ltrim($lines[$current])[0] : '';
81 for ($i = $current, $count = count($lines); $i < $count; $i++) {
82 $line = $lines[$i];
83 // match list marker on the beginning of the line
84 $pattern = ($type === 'ol') ? '/^( {0,'.$leadSpace.'})(\d+)\.[ \t]+/' : '/^( {0,'.$leadSpace.'})\\'.$marker.'[ \t]+/';
85 if (preg_match($pattern, $line, $matches)) {
86 if (($len = substr_count($matches[0], "\t")) > 0) {
87 $indent = str_repeat("\t", $len);
88 $line = substr($line, strlen($matches[0]));
89 } else {
90 $len = strlen($matches[0]);
91 $indent = str_repeat(' ', $len);
92 $line = substr($line, $len);
93 }
94 if ($i === $current) {
95 $leadSpace = strlen($matches[1]) + 1;
96 }
97
98 if ($type === 'ol' && $this->keepListStartNumber) {
99 // attr `start` for ol
100 if (!isset($block['attr']['start']) && isset($matches[2])) {
101 $block['attr']['start'] = $matches[2];
102 }
103 }
104
105 $block['items'][++$item][] = $line;
106 $block['lazyItems'][$item] = $lastLineEmpty;
107 $lastLineEmpty = false;
108 } elseif (ltrim($line) === '') {
109 // line is empty, may be a lazy list
110 $lastLineEmpty = true;
111
112 // two empty lines will end the list
113 if (!isset($lines[$i + 1][0])) {
114 break;
115
116 // next item is the continuation of this list -> lazy list
117 } elseif (preg_match($pattern, $lines[$i + 1])) {
118 $block['items'][$item][] = $line;
119 $block['lazyItems'][$item] = true;
120
121 // next item is indented as much as this list -> lazy list if it is not a reference
122 } elseif (strncmp($lines[$i + 1], $indent, $len) === 0 || !empty($lines[$i + 1]) && $lines[$i + 1][0] == "\t") {
123 $block['items'][$item][] = $line;
124 $nextLine = $lines[$i + 1][0] === "\t" ? substr($lines[$i + 1], 1) : substr($lines[$i + 1], $len);
125 $block['lazyItems'][$item] = empty($nextLine) || !method_exists($this, 'identifyReference') || !$this->identifyReference($nextLine);
126
127 // everything else ends the list
128 } else {
129 break;
130 }
131 } else {
132 if ($line[0] === "\t") {
133 $line = substr($line, 1);
134 } elseif (strncmp($line, $indent, $len) === 0) {
135 $line = substr($line, $len);
136 }
137 $block['items'][$item][] = $line;
138 $lastLineEmpty = false;
139 }
140
141 // if next line is <hr>, end the list
142 if (!empty($lines[$i + 1]) && method_exists($this, 'identifyHr') && $this->identifyHr($lines[$i + 1])) {
143 break;
144 }
145 }
146
147 foreach($block['items'] as $itemId => $itemLines) {
148 $content = [];
149 if (!$block['lazyItems'][$itemId]) {
150 $firstPar = [];
151 while (!empty($itemLines) && rtrim($itemLines[0]) !== '' && $this->detectLineType($itemLines, 0) === 'paragraph') {
152 $firstPar[] = array_shift($itemLines);
153 }
154 $content = $this->parseInline(implode("\n", $firstPar));
155 }
156 if (!empty($itemLines)) {
157 $content = array_merge($content, $this->parseBlocks($itemLines));
158 }
159 $block['items'][$itemId] = $content;
160 }
161
162 return [$block, $i];
163 }
164
165 /**
166 * Renders a list
167 */
168 protected function renderList($block)
169 {
170 $type = $block['list'];
171
172 if (!empty($block['attr'])) {
173 $output = "<$type " . $this->generateHtmlAttributes($block['attr']) . ">\n";
174 } else {
175 $output = "<$type>\n";
176 }
177
178 foreach ($block['items'] as $item => $itemLines) {
179 $output .= '<li>' . $this->renderAbsy($itemLines). "</li>\n";
180 }
181 return $output . "</$type>\n";
182 }
183
184
185 /**
186 * Return html attributes string from [attrName => attrValue] list
187 * @param array $attributes the attribute name-value pairs.
188 * @return string
189 */
190 private function generateHtmlAttributes($attributes)
191 {
192 foreach ($attributes as $name => $value) {
193 $attributes[$name] = "$name=\"$value\"";
194 }
195 return implode(' ', $attributes);
196 }
197
198 abstract protected function parseBlocks($lines);
199 abstract protected function parseInline($text);
200 abstract protected function renderAbsy($absy);
201 abstract protected function detectLineType($lines, $current);
202 }
203