Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Parser.php
001 <?php
002
003 /*
004 * @package s9e\TextFormatter
005 * @copyright Copyright (c) 2010-2016 The s9e Authors
006 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
007 */
008 namespace s9e\TextFormatter\Plugins\Litedown;
009 use s9e\TextFormatter\Parser as Rules;
010 use s9e\TextFormatter\Parser\Tag;
011 use s9e\TextFormatter\Plugins\ParserBase;
012 class Parser extends ParserBase
013 {
014 protected $hasEscapedChars;
015 protected $hasRefs;
016 protected $refs;
017 protected $text;
018 public function parse($text, array $matches)
019 {
020 $this->init($text);
021 $this->matchBlockLevelMarkup();
022 $this->matchLinkReferences();
023 $this->matchInlineCode();
024 $this->matchImages();
025 $this->matchLinks();
026 $this->matchStrikethrough();
027 $this->matchSuperscript();
028 $this->matchEmphasis();
029 $this->matchForcedLineBreaks();
030 unset($this->text);
031 }
032 protected function addImageTag($startTagPos, $endTagPos, $endTagLen, $linkInfo, $alt)
033 {
034 $tag = $this->parser->addTagPair('IMG', $startTagPos, 2, $endTagPos, $endTagLen);
035 $this->setLinkAttributes($tag, $linkInfo, 'src');
036 $tag->setAttribute('alt', $this->decode($alt));
037 $this->overwrite($startTagPos, $endTagPos + $endTagLen - $startTagPos);
038 }
039 protected function addInlineCodeTags($left, $right)
040 {
041 $startTagPos = $left['pos'];
042 $startTagLen = $left['len'] + $left['trimAfter'];
043 $endTagPos = $right['pos'] - $right['trimBefore'];
044 $endTagLen = $right['len'] + $right['trimBefore'];
045 $this->parser->addTagPair('C', $startTagPos, $startTagLen, $endTagPos, $endTagLen);
046 $this->overwrite($startTagPos, $endTagPos + $endTagLen - $startTagPos);
047 }
048 protected function addLinkTag($startTagPos, $endTagPos, $endTagLen, $linkInfo)
049 {
050 $priority = ($endTagLen === 1) ? 1 : -1;
051 $tag = $this->parser->addTagPair('URL', $startTagPos, 1, $endTagPos, $endTagLen, $priority);
052 $this->setLinkAttributes($tag, $linkInfo, 'url');
053 $this->overwrite($startTagPos, 1);
054 $this->overwrite($endTagPos, $endTagLen);
055 }
056 protected function closeList(array $list, $textBoundary)
057 {
058 $this->parser->addEndTag('LIST', $textBoundary, 0)->pairWith($list['listTag']);
059 $this->parser->addEndTag('LI', $textBoundary, 0)->pairWith($list['itemTag']);
060 if ($list['tight'])
061 foreach ($list['itemTags'] as $itemTag)
062 $itemTag->removeFlags(Rules::RULE_CREATE_PARAGRAPHS);
063 }
064 protected function computeQuoteIgnoreLen($str, $maxQuoteDepth)
065 {
066 $remaining = $str;
067 while (--$maxQuoteDepth >= 0)
068 $remaining = \preg_replace('/^ *> ?/', '', $remaining);
069 return \strlen($str) - \strlen($remaining);
070 }
071 protected function decode($str)
072 {
073 if ($this->config['decodeHtmlEntities'] && \strpos($str, '&') !== \false)
074 $str = \html_entity_decode($str, \ENT_QUOTES, 'UTF-8');
075 $str = \str_replace("\x1A", '', $str);
076 if ($this->hasEscapedChars)
077 $str = \strtr(
078 $str,
079 array(
080 "\x1B0" => '!', "\x1B1" => '"', "\x1B2" => "'", "\x1B3" => '(',
081 "\x1B4" => ')', "\x1B5" => '*', "\x1B6" => '[', "\x1B7" => '\\',
082 "\x1B8" => ']', "\x1B9" => '^', "\x1BA" => '_', "\x1BB" => '`',
083 "\x1BC" => '~'
084 )
085 );
086 return $str;
087 }
088 protected function encode($str)
089 {
090 return \strtr(
091 $str,
092 array(
093 '\\!' => "\x1B0", '\\"' => "\x1B1", "\\'" => "\x1B2", '\\(' => "\x1B3",
094 '\\)' => "\x1B4", '\\*' => "\x1B5", '\\[' => "\x1B6", '\\\\' => "\x1B7",
095 '\\]' => "\x1B8", '\\^' => "\x1B9", '\\_' => "\x1BA", '\\`' => "\x1BB",
096 '\\~' => "\x1BC"
097 )
098 );
099 }
100 protected function getAtxHeaderEndTagLen($startPos, $endPos)
101 {
102 $content = \substr($this->text, $startPos, $endPos - $startPos);
103 \preg_match('/[ \\t]*#*[ \\t]*$/', $content, $m);
104 return \strlen($m[0]);
105 }
106 protected function getSetextLines()
107 {
108 $setextLines = array();
109 if (\strpos($this->text, '-') === \false && \strpos($this->text, '=') === \false)
110 return $setextLines;
111 $regexp = '/^(?=[-=>])(?:> ?)*(?=[-=])(?:-+|=+) *$/m';
112 if (\preg_match_all($regexp, $this->text, $matches, \PREG_OFFSET_CAPTURE))
113 foreach ($matches[0] as $_f570d26d)
114 {
115 list($match, $matchPos) = $_f570d26d;
116 $endTagPos = $matchPos - 1;
117 while ($endTagPos > 0 && $this->text[$endTagPos - 1] === ' ')
118 --$endTagPos;
119 $setextLines[$matchPos - 1] = array(
120 'endTagLen' => $matchPos + \strlen($match) - $endTagPos,
121 'endTagPos' => $endTagPos,
122 'quoteDepth' => \substr_count($match, '>'),
123 'tagName' => ($match[0] === '=') ? 'H1' : 'H2'
124 );
125 }
126 return $setextLines;
127 }
128 protected function getEmphasisByBlock($regexp, $pos)
129 {
130 $block = array();
131 $blocks = array();
132 $breakPos = \strpos($this->text, "\x17", $pos);
133 \preg_match_all($regexp, $this->text, $matches, \PREG_OFFSET_CAPTURE, $pos);
134 foreach ($matches[0] as $m)
135 {
136 $matchPos = $m[1];
137 $matchLen = \strlen($m[0]);
138 if ($matchPos > $breakPos)
139 {
140 $blocks[] = $block;
141 $block = array();
142 $breakPos = \strpos($this->text, "\x17", $matchPos);
143 }
144 if (!$this->ignoreEmphasis($matchPos, $matchLen))
145 $block[] = array($matchPos, $matchLen);
146 }
147 $blocks[] = $block;
148 return $blocks;
149 }
150 protected function getInlineCodeMarkers()
151 {
152 $pos = \strpos($this->text, '`');
153 if ($pos === \false)
154 return array();
155 \preg_match_all(
156 '/(`+)(\\s*)[^\\x17`]*/',
157 \str_replace("\x1BB", '\\`', $this->text),
158 $matches,
159 \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER,
160 $pos
161 );
162 $trimNext = 0;
163 $markers = array();
164 foreach ($matches as $m)
165 {
166 $markers[] = array(
167 'pos' => $m[0][1],
168 'len' => \strlen($m[1][0]),
169 'trimBefore' => $trimNext,
170 'trimAfter' => \strlen($m[2][0]),
171 'next' => $m[0][1] + \strlen($m[0][0])
172 );
173 $trimNext = \strlen($m[0][0]) - \strlen(\rtrim($m[0][0]));
174 }
175 return $markers;
176 }
177 protected function getLabels()
178 {
179 \preg_match_all(
180 '/\\[((?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*)\\]/',
181 $this->text,
182 $matches,
183 \PREG_OFFSET_CAPTURE
184 );
185 $labels = array();
186 foreach ($matches[1] as $m)
187 $labels[$m[1] - 1] = \strtolower($m[0]);
188 return $labels;
189 }
190 protected function ignoreEmphasis($matchPos, $matchLen)
191 {
192 return ($this->text[$matchPos] === '_' && $matchLen === 1 && $this->isSurroundedByAlnum($matchPos, $matchLen));
193 }
194 protected function init($text)
195 {
196 if (\strpos($text, '\\') === \false || !\preg_match('/\\\\[!"\'()*[\\\\\\]^_`~]/', $text))
197 $this->hasEscapedChars = \false;
198 else
199 {
200 $this->hasEscapedChars = \true;
201 $text = $this->encode($text);
202 }
203 $text .= "\n\n\x17";
204 $this->text = $text;
205 }
206 protected function isAlnum($chr)
207 {
208 return (\strpos(' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', $chr) > 0);
209 }
210 protected function isSurroundedByAlnum($matchPos, $matchLen)
211 {
212 return ($matchPos > 0 && $this->isAlnum($this->text[$matchPos - 1]) && $this->isAlnum($this->text[$matchPos + $matchLen]));
213 }
214 protected function markBoundary($pos)
215 {
216 $this->text[$pos] = "\x17";
217 }
218 protected function matchBlockLevelMarkup()
219 {
220 $codeFence = \null;
221 $codeIndent = 4;
222 $codeTag = \null;
223 $lineIsEmpty = \true;
224 $lists = array();
225 $listsCnt = 0;
226 $newContext = \false;
227 $quotes = array();
228 $quotesCnt = 0;
229 $setextLines = $this->getSetextLines();
230 $textBoundary = 0;
231 $regexp = '/^(?:(?=[-*+\\d \\t>`~#_])((?: {0,3}> ?)+)?([ \\t]+)?(\\* *\\* *\\*[* ]*$|- *- *-[- ]*$|_ *_ *_[_ ]*$|=+$)?((?:[-*+]|\\d+\\.)[ \\t]+(?=\\S))?[ \\t]*(#{1,6}[ \\t]+|```+[^`\\n]*$|~~~+[^~\\n]*$)?)?/m';
232 \preg_match_all($regexp, $this->text, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER);
233 foreach ($matches as $m)
234 {
235 $matchPos = $m[0][1];
236 $matchLen = \strlen($m[0][0]);
237 $ignoreLen = 0;
238 $quoteDepth = 0;
239 $continuation = !$lineIsEmpty;
240 $lfPos = \strpos($this->text, "\n", $matchPos);
241 $lineIsEmpty = ($lfPos === $matchPos + $matchLen && empty($m[3][0]) && empty($m[4][0]) && empty($m[5][0]));
242 $breakParagraph = ($lineIsEmpty && $continuation);
243 if (!empty($m[1][0]))
244 {
245 $quoteDepth = \substr_count($m[1][0], '>');
246 $ignoreLen = \strlen($m[1][0]);
247 if (isset($codeTag) && $codeTag->hasAttribute('quoteDepth'))
248 {
249 $quoteDepth = \min($quoteDepth, $codeTag->getAttribute('quoteDepth'));
250 $ignoreLen = $this->computeQuoteIgnoreLen($m[1][0], $quoteDepth);
251 }
252 $this->overwrite($matchPos, $ignoreLen);
253 }
254 if ($quoteDepth < $quotesCnt && !$continuation)
255 {
256 $newContext = \true;
257 do
258 {
259 $this->parser->addEndTag('QUOTE', $textBoundary, 0)
260 ->pairWith(\array_pop($quotes));
261 }
262 while ($quoteDepth < --$quotesCnt);
263 }
264 if ($quoteDepth > $quotesCnt && !$lineIsEmpty)
265 {
266 $newContext = \true;
267 do
268 {
269 $tag = $this->parser->addStartTag('QUOTE', $matchPos, 0, $quotesCnt - 999);
270 $quotes[] = $tag;
271 }
272 while ($quoteDepth > ++$quotesCnt);
273 }
274 $indentWidth = 0;
275 $indentPos = 0;
276 if (!empty($m[2][0]) && !$codeFence)
277 {
278 $indentStr = $m[2][0];
279 $indentLen = \strlen($indentStr);
280 do
281 {
282 if ($indentStr[$indentPos] === ' ')
283 ++$indentWidth;
284 else
285 $indentWidth = ($indentWidth + 4) & ~3;
286 }
287 while (++$indentPos < $indentLen && $indentWidth < $codeIndent);
288 }
289 if (isset($codeTag) && !$codeFence && $indentWidth < $codeIndent && !$lineIsEmpty)
290 $newContext = \true;
291 if ($newContext)
292 {
293 $newContext = \false;
294 if (isset($codeTag))
295 {
296 $this->overwrite($codeTag->getPos(), $textBoundary - $codeTag->getPos());
297 $endTag = $this->parser->addEndTag('CODE', $textBoundary, 0, -1);
298 $endTag->pairWith($codeTag);
299 $codeTag = \null;
300 $codeFence = \null;
301 }
302 foreach ($lists as $list)
303 $this->closeList($list, $textBoundary);
304 $lists = array();
305 $listsCnt = 0;
306 if ($matchPos)
307 $this->markBoundary($matchPos - 1);
308 }
309 if ($indentWidth >= $codeIndent)
310 {
311 if (isset($codeTag) || !$continuation)
312 {
313 $ignoreLen += $indentPos;
314 if (!isset($codeTag))
315 $codeTag = $this->parser->addStartTag('CODE', $matchPos + $ignoreLen, 0, -999);
316 $m = array();
317 }
318 }
319 else
320 {
321 $hasListItem = !empty($m[4][0]);
322 if (!$indentWidth && !$continuation && !$hasListItem)
323 $listIndex = -1;
324 elseif ($continuation && !$hasListItem)
325 $listIndex = $listsCnt - 1;
326 elseif (!$listsCnt)
327 if ($hasListItem && (!$continuation || $this->text[$matchPos - 1] === "\x17"))
328 $listIndex = 0;
329 else
330 $listIndex = -1;
331 else
332 {
333 $listIndex = 0;
334 while ($listIndex < $listsCnt && $indentWidth > $lists[$listIndex]['maxIndent'])
335 ++$listIndex;
336 }
337 while ($listIndex < $listsCnt - 1)
338 {
339 $this->closeList(\array_pop($lists), $textBoundary);
340 --$listsCnt;
341 }
342 if ($listIndex === $listsCnt && !$hasListItem)
343 --$listIndex;
344 if ($hasListItem && $listIndex >= 0)
345 {
346 $breakParagraph = \true;
347 $tagPos = $matchPos + $ignoreLen + $indentPos;
348 $tagLen = \strlen($m[4][0]);
349 $itemTag = $this->parser->addStartTag('LI', $tagPos, $tagLen);
350 $this->overwrite($tagPos, $tagLen);
351 if ($listIndex < $listsCnt)
352 {
353 $this->parser->addEndTag('LI', $textBoundary, 0)
354 ->pairWith($lists[$listIndex]['itemTag']);
355 $lists[$listIndex]['itemTag'] = $itemTag;
356 $lists[$listIndex]['itemTags'][] = $itemTag;
357 }
358 else
359 {
360 ++$listsCnt;
361 if ($listIndex)
362 {
363 $minIndent = $lists[$listIndex - 1]['maxIndent'] + 1;
364 $maxIndent = \max($minIndent, $listIndex * 4);
365 }
366 else
367 {
368 $minIndent = 0;
369 $maxIndent = $indentWidth;
370 }
371 $listTag = $this->parser->addStartTag('LIST', $tagPos, 0);
372 if (\strpos($m[4][0], '.') !== \false)
373 {
374 $listTag->setAttribute('type', 'decimal');
375 $start = (int) $m[4][0];
376 if ($start !== 1)
377 $listTag->setAttribute('start', $start);
378 }
379 $lists[] = array(
380 'listTag' => $listTag,
381 'itemTag' => $itemTag,
382 'itemTags' => array($itemTag),
383 'minIndent' => $minIndent,
384 'maxIndent' => $maxIndent,
385 'tight' => \true
386 );
387 }
388 }
389 if ($listsCnt && !$continuation && !$lineIsEmpty)
390 if (\count($lists[0]['itemTags']) > 1 || !$hasListItem)
391 {
392 foreach ($lists as &$list)
393 $list['tight'] = \false;
394 unset($list);
395 }
396 $codeIndent = ($listsCnt + 1) * 4;
397 }
398 if (isset($m[5]))
399 {
400 if ($m[5][0][0] === '#')
401 {
402 $startTagLen = \strlen($m[5][0]);
403 $startTagPos = $matchPos + $matchLen - $startTagLen;
404 $endTagLen = $this->getAtxHeaderEndTagLen($matchPos + $matchLen, $lfPos);
405 $endTagPos = $lfPos - $endTagLen;
406 $this->parser->addTagPair('H' . \strspn($m[5][0], '#', 0, 6), $startTagPos, $startTagLen, $endTagPos, $endTagLen);
407 $this->markBoundary($startTagPos);
408 $this->markBoundary($lfPos);
409 if ($continuation)
410 $breakParagraph = \true;
411 }
412 elseif ($m[5][0][0] === '`' || $m[5][0][0] === '~')
413 {
414 $tagPos = $matchPos + $ignoreLen;
415 $tagLen = $lfPos - $tagPos;
416 if (isset($codeTag) && $m[5][0] === $codeFence)
417 {
418 $endTag = $this->parser->addEndTag('CODE', $tagPos, $tagLen, -1);
419 $endTag->pairWith($codeTag);
420 $this->parser->addIgnoreTag($textBoundary, $tagPos - $textBoundary);
421 $this->overwrite($codeTag->getPos(), $tagPos + $tagLen - $codeTag->getPos());
422 $codeTag = \null;
423 $codeFence = \null;
424 }
425 elseif (!isset($codeTag))
426 {
427 $codeTag = $this->parser->addStartTag('CODE', $tagPos, $tagLen);
428 $codeFence = \substr($m[5][0], 0, \strspn($m[5][0], '`~'));
429 $codeTag->setAttribute('quoteDepth', $quoteDepth);
430 $this->parser->addIgnoreTag($tagPos + $tagLen, 1);
431 $lang = \trim(\trim($m[5][0], '`~'));
432 if ($lang !== '')
433 $codeTag->setAttribute('lang', $lang);
434 }
435 }
436 }
437 elseif (!empty($m[3][0]) && !$listsCnt && $this->text[$matchPos + $matchLen] !== "\x17")
438 {
439 $this->parser->addSelfClosingTag('HR', $matchPos + $ignoreLen, $matchLen - $ignoreLen);
440 $breakParagraph = \true;
441 $this->markBoundary($lfPos);
442 }
443 elseif (isset($setextLines[$lfPos]) && $setextLines[$lfPos]['quoteDepth'] === $quoteDepth && !$lineIsEmpty && !$listsCnt && !isset($codeTag))
444 {
445 $this->parser->addTagPair(
446 $setextLines[$lfPos]['tagName'],
447 $matchPos + $ignoreLen,
448 0,
449 $setextLines[$lfPos]['endTagPos'],
450 $setextLines[$lfPos]['endTagLen']
451 );
452 $this->markBoundary($setextLines[$lfPos]['endTagPos'] + $setextLines[$lfPos]['endTagLen']);
453 }
454 if ($breakParagraph)
455 {
456 $this->parser->addParagraphBreak($textBoundary);
457 $this->markBoundary($textBoundary);
458 }
459 if (!$lineIsEmpty)
460 $textBoundary = $lfPos;
461 if ($ignoreLen)
462 $this->parser->addIgnoreTag($matchPos, $ignoreLen, 1000);
463 }
464 }
465 protected function matchEmphasis()
466 {
467 $this->matchEmphasisByCharacter('*', '/\\*+/');
468 $this->matchEmphasisByCharacter('_', '/_+/');
469 }
470 protected function matchEmphasisByCharacter($character, $regexp)
471 {
472 $pos = \strpos($this->text, $character);
473 if ($pos === \false)
474 return;
475 foreach ($this->getEmphasisByBlock($regexp, $pos) as $block)
476 $this->processEmphasisBlock($block);
477 }
478 protected function matchForcedLineBreaks()
479 {
480 $pos = \strpos($this->text, " \n");
481 while ($pos !== \false)
482 {
483 $this->parser->addBrTag($pos + 2);
484 $pos = \strpos($this->text, " \n", $pos + 3);
485 }
486 }
487 protected function matchImages()
488 {
489 $pos = \strpos($this->text, ' !== \false)
493 $this->matchInlineImages();
494 if ($this->hasRefs)
495 $this->matchReferenceImages();
496 }
497 protected function matchInlineImages()
498 {
499 \preg_match_all(
500 '/!\\[(?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*\\]\\(( *(?:[^\\x17\\s()]|\\([^\\x17\\s()]*\\))*(?=[ )]) *(?:"[^\\x17]*?"|\'[^\\x17]*?\'|\\([^\\x17)]*\\))? *)\\)/',
501 $this->text,
502 $matches,
503 \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER
504 );
505 foreach ($matches as $m)
506 {
507 $linkInfo = $m[1][0];
508 $startTagPos = $m[0][1];
509 $endTagLen = 3 + \strlen($linkInfo);
510 $endTagPos = $startTagPos + \strlen($m[0][0]) - $endTagLen;
511 $alt = \substr($m[0][0], 2, \strlen($m[0][0]) - $endTagLen - 2);
512 $this->addImageTag($startTagPos, $endTagPos, $endTagLen, $linkInfo, $alt);
513 }
514 }
515 protected function matchReferenceImages()
516 {
517 \preg_match_all(
518 '/!\\[((?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*)\\](?: ?\\[([^\\x17[\\]]+)\\])?/',
519 $this->text,
520 $matches,
521 \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER
522 );
523 foreach ($matches as $m)
524 {
525 $startTagPos = $m[0][1];
526 $endTagPos = $startTagPos + 2 + \strlen($m[1][0]);
527 $endTagLen = 1;
528 $alt = $m[1][0];
529 $id = $alt;
530 if (isset($m[2][0], $this->refs[$m[2][0]]))
531 {
532 $endTagLen = \strlen($m[0][0]) - \strlen($alt) - 2;
533 $id = $m[2][0];
534 }
535 elseif (!isset($this->refs[$id]))
536 continue;
537 $this->addImageTag($startTagPos, $endTagPos, $endTagLen, $this->refs[$id], $alt);
538 }
539 }
540 protected function matchInlineCode()
541 {
542 $markers = $this->getInlineCodeMarkers();
543 $i = -1;
544 $cnt = \count($markers);
545 while (++$i < ($cnt - 1))
546 {
547 $pos = $markers[$i]['next'];
548 $j = $i;
549 if ($this->text[$markers[$i]['pos']] !== '`')
550 {
551 ++$markers[$i]['pos'];
552 --$markers[$i]['len'];
553 }
554 while (++$j < $cnt && $markers[$j]['pos'] === $pos)
555 {
556 if ($markers[$j]['len'] === $markers[$i]['len'])
557 {
558 $this->addInlineCodeTags($markers[$i], $markers[$j]);
559 $i = $j;
560 break;
561 }
562 $pos = $markers[$j]['next'];
563 }
564 }
565 }
566 protected function matchInlineLinks()
567 {
568 \preg_match_all(
569 '/\\[(?:[^\\x17[\\]]|\\[[^\\x17[\\]]*\\])*\\]\\(( *(?:[^\\x17\\s()]|\\([^\\x17\\s()]*\\))*(?=[ )]) *(?:"[^\\x17]*?"|\'[^\\x17]*?\'|\\([^\\x17)]*\\))? *)\\)/',
570 $this->text,
571 $matches,
572 \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER
573 );
574 foreach ($matches as $m)
575 {
576 $linkInfo = $m[1][0];
577 $startTagPos = $m[0][1];
578 $endTagLen = 3 + \strlen($linkInfo);
579 $endTagPos = $startTagPos + \strlen($m[0][0]) - $endTagLen;
580 $this->addLinkTag($startTagPos, $endTagPos, $endTagLen, $linkInfo);
581 }
582 }
583 protected function matchLinkReferences()
584 {
585 $this->hasRefs = \false;
586 $this->refs = array();
587 if (\strpos($this->text, ']:') === \false)
588 return;
589 $regexp = '/^\\x1A* {0,3}\\[([^\\x17\\]]+)\\]: *([^\\s\\x17]+ *(?:"[^\\x17]*?"|\'[^\\x17]*?\'|\\([^\\x17)]*\\))?)[^\\x17\\n]*\\n?/m';
590 \preg_match_all($regexp, $this->text, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER);
591 foreach ($matches as $m)
592 {
593 $this->parser->addIgnoreTag($m[0][1], \strlen($m[0][0]), -2);
594 $id = \strtolower($m[1][0]);
595 if (isset($this->refs[$id]))
596 continue;
597 $this->hasRefs = \true;
598 $this->refs[$id] = $m[2][0];
599 }
600 }
601 protected function matchLinks()
602 {
603 if (\strpos($this->text, '](') !== \false)
604 $this->matchInlineLinks();
605 if ($this->hasRefs)
606 $this->matchReferenceLinks();
607 }
608 protected function matchReferenceLinks()
609 {
610 $labels = $this->getLabels();
611 foreach ($labels as $startTagPos => $id)
612 {
613 $labelPos = $startTagPos + 2 + \strlen($id);
614 $endTagPos = $labelPos - 1;
615 $endTagLen = 1;
616 if ($this->text[$labelPos] === ' ')
617 ++$labelPos;
618 if (isset($labels[$labelPos], $this->refs[$labels[$labelPos]]))
619 {
620 $id = $labels[$labelPos];
621 $endTagLen = $labelPos + 2 + \strlen($id) - $endTagPos;
622 }
623 if (isset($this->refs[$id]))
624 $this->addLinkTag($startTagPos, $endTagPos, $endTagLen, $this->refs[$id]);
625 }
626 }
627 protected function matchStrikethrough()
628 {
629 $pos = \strpos($this->text, '~~');
630 if ($pos === \false)
631 return;
632 \preg_match_all(
633 '/~~[^\\x17]+?~~/',
634 $this->text,
635 $matches,
636 \PREG_OFFSET_CAPTURE,
637 $pos
638 );
639 foreach ($matches[0] as $_4b034d25)
640 {
641 list($match, $matchPos) = $_4b034d25;
642 $matchLen = \strlen($match);
643 $this->parser->addTagPair('DEL', $matchPos, 2, $matchPos + $matchLen - 2, 2);
644 }
645 }
646 protected function matchSuperscript()
647 {
648 $pos = \strpos($this->text, '^');
649 if ($pos === \false)
650 return;
651 \preg_match_all(
652 '/\\^[^\\x17\\s]++/',
653 $this->text,
654 $matches,
655 \PREG_OFFSET_CAPTURE,
656 $pos
657 );
658 foreach ($matches[0] as $_4b034d25)
659 {
660 list($match, $matchPos) = $_4b034d25;
661 $matchLen = \strlen($match);
662 $startTagPos = $matchPos;
663 $endTagPos = $matchPos + $matchLen;
664 $parts = \explode('^', $match);
665 unset($parts[0]);
666 foreach ($parts as $part)
667 {
668 $this->parser->addTagPair('SUP', $startTagPos, 1, $endTagPos, 0);
669 $startTagPos += 1 + \strlen($part);
670 }
671 }
672 }
673 protected function overwrite($pos, $len)
674 {
675 $this->text = \substr($this->text, 0, $pos) . \str_repeat("\x1A", $len) . \substr($this->text, $pos + $len);
676 }
677 protected function processEmphasisBlock(array $block)
678 {
679 $buffered = 0;
680 $emPos = -1;
681 $strongPos = -1;
682 foreach ($block as $_aab3a45e)
683 {
684 list($matchPos, $matchLen) = $_aab3a45e;
685 $closeLen = \min(3, $matchLen);
686 $closeEm = $closeLen & $buffered & 1;
687 $closeStrong = $closeLen & $buffered & 2;
688 $emEndPos = $matchPos;
689 $strongEndPos = $matchPos;
690 if ($buffered > 2 && $emPos === $strongPos)
691 if ($closeEm)
692 $emPos += 2;
693 else
694 ++$strongPos;
695 if ($closeEm && $closeStrong)
696 if ($emPos < $strongPos)
697 $emEndPos += 2;
698 else
699 ++$strongEndPos;
700 $remaining = $matchLen;
701 if ($closeEm)
702 {
703 --$buffered;
704 --$remaining;
705 $this->parser->addTagPair('EM', $emPos, 1, $emEndPos, 1);
706 }
707 if ($closeStrong)
708 {
709 $buffered -= 2;
710 $remaining -= 2;
711 $this->parser->addTagPair('STRONG', $strongPos, 2, $strongEndPos, 2);
712 }
713 $remaining = \min(3, $remaining);
714 if ($remaining & 1)
715 $emPos = $matchPos + $matchLen - $remaining;
716 if ($remaining & 2)
717 $strongPos = $matchPos + $matchLen - $remaining;
718 $buffered += $remaining;
719 }
720 }
721 protected function setLinkAttributes(Tag $tag, $linkInfo, $attrName)
722 {
723 $url = \trim($linkInfo);
724 $title = '';
725 $pos = \strpos($url, ' ');
726 if ($pos !== \false)
727 {
728 $title = \substr(\trim(\substr($url, $pos)), 1, -1);
729 $url = \substr($url, 0, $pos);
730 }
731 $tag->setAttribute($attrName, $this->decode($url));
732 if ($title > '')
733 $tag->setAttribute('title', $this->decode($title));
734 }
735 }