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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Parser.php

Zuletzt modifiziert: 09.10.2024, 12:58 - Dateigröße: 21.67 KiB


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, '![');
490          if ($pos === \false)
491              return;
492          if (\strpos($this->text, '](', $pos) !== \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  }