Exemplo n.º 1
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be appended to the given target. Any remaining content should be passed to the
  * next parser in the chain.
  *
  * @param Text $content
  * @param InlineNodeAcceptorInterface $target
  * @return void
  */
 public function parseInline(Text $content, InlineNodeAcceptorInterface $target)
 {
     $content->handle('{
             (?<!\\\\)
             (
                 \\<[A-Z][A-Z0-9]*            # an opening HTML tag with
                     (?:                         # multiple attributes...
                         \\s+
                         [A-Z_:][A-Z0-9_:.\\-]*
                         (?:
                             \\s*=\\s*
                             (?:
                                 [^ "\'=<>`]+|   # unquoted attribute values
                                 \'[^\']*\'|     # single-quoted attribute values
                                 "[^"]*"         # double-quoted attribute values
                             )
                         )?
                     )*
                     \\s*/?\\>|                # ...or
                 \\</[A-Z][A-Z0-9]*\\s*\\>|     # a closing HTML tag, or
                 \\<!--(-(?!-)|[^\\-])*?--\\>|  # a HTML comment, or
                 \\<\\?.*?\\?\\>|                # a processing instruction, or
                 \\<![A-Z]+\\s+[^>]+\\>|        # an element type declaration, or
                 \\<!\\[CDATA\\[.*?\\]\\]\\>       # a CDATA section
             )
         }isx', function (Text $content) use($target) {
         $target->addInline(new RawHTML($content));
     }, function (Text $part) use($target) {
         $this->next->parseInline($part, $target);
     });
 }
Exemplo n.º 2
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $content->handle('/
             (
                 (?:                             # We are either at the beginning of the match
                     ^|\\n                        # or at a newline (for multi-line quotes)
                 )
                 (?:
                     [ ]{0,3}\\>[ ]*              # either a blank line
                   |
                     [ ]{0,3}\\>[ ]?[`~]{3,}.*    # or a code fence
                   |
                     [ ]{0,3}\\>                  # or non-blank lines...
                     [ ]*[^\\n\\ ][^\\n]*[ ]*
                     (                           # ...with lazy continuation
                         \\n
                         [ ]{0,3}
                         [^>\\-+*=\\ \\n][^\\n]*
                     )*
                 )
             )+
             $
         /mx', function (Text $content) use($target) {
         // Remove block quote marker plus surrounding whitespace on each line
         $content->replace('/^[ ]{0,3}\\>[ ]?/m', '');
         $blockquote = new Blockquote();
         $target->addChild($blockquote);
         $this->first->parseBlock($content, $blockquote);
     }, function (Text $part) use($target) {
         $this->next->parseBlock($part, $target);
     });
 }
Exemplo n.º 3
0
 protected function parseEmail(Text $content, InlineNodeAcceptorInterface $target)
 {
     if ($content->contains('@')) {
         $content->handle('{
                 <
                 (?:mailto:)?
                 (
                     [a-zA-Z0-9.!#$%&\'*+/=?^_`{|}~-]+
                     \\@
                     [a-zA-Z0-9]
                     (?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
                     (?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*
                 )
                 >
             }ix', function (Text $w, Text $email) use($target) {
             $text = $email->copy();
             $link = new Link($email->prepend('mailto:'));
             $link->addInline(new String($text));
             $target->addInline($link);
         }, function (Text $part) use($target) {
             $this->next->parseInline($part, $target);
         });
     } else {
         $this->next->parseInline($content, $target);
     }
 }
Exemplo n.º 4
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $content->handle('{
             ^(?:
                 ([ ]{0,3})                  #1 Initial indentation
                 (                           #2 Fence
                     ([`~])                  #3 The fence character (` or ~)
                     \\3{2,}                  #  At least two remaining fence characters
                 )
                 ([^`\\n]*?)?                 #4 Code language [optional]
                 (?:
                     \\n(.*?)                 #5 Code block
                 )?
                 (?:(?<=\\n)([ ]{0,3}\\2\\3*[ ]*)|\\z)  #  Closing fence or end of document
             )$
         }msx', function (Text $whole, Text $whitespace, Text $fence, Text $fenceChar, Text $lang, Text $code = null) use($target) {
         $leading = $whitespace->getLength();
         $code = $code ?: new Text();
         $language = new Text(explode(' ', $lang->trim())[0]);
         $language->decodeEntities();
         // Remove all leading whitespace from content lines
         if ($leading > 0) {
             $code->replace("/^[ ]{0,{$leading}}/m", '');
         }
         $target->addChild(new CodeBlock($code, $language));
     }, function (Text $part) use($target) {
         $this->next->parseBlock($part, $target);
     });
 }
Exemplo n.º 5
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $content->handle('{
             (?:                 # Ensure blank line before (or beginning of subject)
                 \\A\\s*\\n?|
                 \\n\\s*\\n
             )
             [ ]{4}              # four leading spaces
             .+
             (?:                 # optionally more spaces
                 (?:             # blank lines in between are okay
                     \\n[ ]*
                 )*
                 \\n
                 [ ]{4}
                 .+
             )*
             $
         }mx', function (Text $code) use($target) {
         // Remove leading blank lines
         $code->replace('/^(\\s*\\n)*/', '');
         // Remove indent
         $code->replace('/^[ ]{1,4}/m', '');
         $code->append("\n");
         $target->addChild(new CodeBlock($code));
     }, function (Text $part) use($target) {
         $this->next->parseBlock($part, $target);
     });
 }
Exemplo n.º 6
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be appended to the given target. Any remaining content should be passed to the
  * next parser in the chain.
  *
  * @param Text $content
  * @param InlineNodeAcceptorInterface $target
  * @return void
  */
 public function parseInline(Text $content, InlineNodeAcceptorInterface $target)
 {
     $content->handle('/(\\\\\\n)|( {2,}\\n)/', function () use($target) {
         $target->addInline(new HardBreak());
     }, function (Text $part) use($target) {
         $this->next->parseInline($part, $target);
     });
 }
Exemplo n.º 7
0
 protected function parseUnderscores(Text $content, InlineNodeAcceptorInterface $target)
 {
     $content->handle('{ (?<![A-Za-z0-9]) (__) (?![\\s_]) (.+) (?<![\\s_]) \\1 (?![A-Za-z0-9]) }sx', function (Text $w, Text $a, Text $inner) use($target) {
         $emphasis = new StrongEmphasis($inner);
         $this->context->queue($emphasis->getContent(), $emphasis);
         $target->addInline($emphasis);
     }, function (Text $part) use($target) {
         $this->next->parseInline($part, $target);
     });
 }
Exemplo n.º 8
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $content->handle('/^\\s*$/m', function (Text $line) use($target) {
         $target->addChild(new BlankLine($line));
     }, function (Text $part) use($target) {
         $paragraph = new Paragraph($part);
         $target->addChild($paragraph);
         $this->inlineParser->queue($paragraph->getText(), $paragraph);
     });
 }
Exemplo n.º 9
0
 /**
  * Preprocess the text.
  *
  * This unifies line endings and replaces tabs by spaces.
  *
  * @param Text $text
  * @return void
  */
 protected function prepare(Text $text)
 {
     // Unify line endings
     $text->replaceString("\r\n", "\n");
     $text->replaceString("\r", "\n");
     // Replace tabs by spaces
     $text->replace('/(.*?)\\t/', function (Text $whole, Text $string) {
         $tabWidth = 4;
         return $string . str_repeat(' ', $tabWidth - $string->getLength() % $tabWidth);
     });
 }
Exemplo n.º 10
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $content->handle('{
             ^
             [ ]{0,3}\\[(.+)\\]:  # id = $1
               [ \\t]*
               \\n?               # maybe *one* newline
               [ \\t]*
             (?|                 # url = $2
               ([^\\s<>]+)            # either without spaces
               |
               <([^<>\\n]*)>         # or enclosed in angle brackets
             )
             (?:
               [ \\t]*
               \\n?               # maybe one newline
               [ \\t]*
                 (?<=\\s)         # lookbehind for whitespace
                 ["\'(]
                 (.+?)           # title = $3
                 ["\')]
                 [ \\t]*
             )?  # title is optional
             $
         }xm', function (Text $whole, Text $id, Text $url, Text $title = null) use($target) {
         if (!$this->queue->isEmpty()) {
             $lastPart = $this->queue->dequeue();
             // If the previous line was not empty, we should not parse this as a link reference
             if (!$lastPart->match('/(^|\\n *)\\n$/')) {
                 $lastPart->append($whole);
                 $this->queue->enqueue($lastPart);
                 return;
             }
             $this->parsePart($lastPart, $target);
         }
         $id = $id->lower()->getString();
         // Throw away duplicate reference definitions
         if (!$this->links->exists($id)) {
             $url->decodeEntities();
             // Replace special characters in the URL
             $url->encodeUrl();
             $this->links->set($id, $url);
             if ($title) {
                 $title->decodeEntities();
                 $this->titles->set($id, $title);
             }
         }
     }, function (Text $part) {
         if (!$this->queue->isEmpty()) {
             $lastPart = $this->queue->dequeue();
             $part->prepend($lastPart);
         }
         $this->queue->enqueue($part);
     });
     if (!$this->queue->isEmpty()) {
         $lastPart = $this->queue->dequeue();
         $this->parsePart($lastPart, $target);
     }
 }
Exemplo n.º 11
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $content->handle('{
             ^[ ]{0,3}       # Optional leading spaces
             (\\#{1,6})       # $1 = string of #\'s
             (([ ].+?)??)    # $2 = Header text
             ([ ]\\#*[ ]*)?   # optional closing #\'s (not counted)
             $
         }mx', function (Text $whole, Text $marks, Text $content) use($target) {
         $level = $marks->getLength();
         $heading = new Heading($content->trim(), $level);
         $target->addChild($heading);
         $this->inlineParser->queue($heading->getText(), $heading);
     }, function (Text $part) use($target) {
         $this->next->parseBlock($part, $target);
     });
 }
Exemplo n.º 12
0
 protected function parseCodeSpan(Text $content, InlineNodeAcceptorInterface $target)
 {
     $content->handle('{
             (?<![`\\\\])
             (`+)        # $1 = Opening run of `
             (?!`)
             (.+?)       # $2 = The code block
             (?<!`)
             \\1          # Matching closer
             (?!`)
         }sx', function (Text $whole, Text $b, Text $code) use($target) {
         // Replace multiple whitespace characters in a row with a single space
         $code->trim()->replaceString("\n", ' ')->replace('/[\\s]{2,}/', ' ');
         $target->addInline(new Code($code->escapeHtml()));
     }, function (Text $part) use($target) {
         $this->next->parseInline($part, $target);
     });
 }
Exemplo n.º 13
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $content->handle('{
             ^
             [ ]{0,3}
             ([^>\\-*=\\ \\n].*)
             [ ]*
             \\n
             [ ]{0,3}
             (=+|-+)
             [ ]*
             \\n*
             $
         }mx', function (Text $whole, Text $content, Text $mark) use($target) {
         $level = substr($mark, 0, 1) == '=' ? 1 : 2;
         $heading = new Heading($content->trim(), $level);
         $target->addChild($heading);
         $this->inlineParser->queue($heading->getText(), $heading);
     }, function (Text $part) use($target) {
         $this->next->parseBlock($part, $target);
     });
 }
Exemplo n.º 14
0
 /**
  * Parse the given content.
  *
  * Any newly created nodes should be pushed to the stack. Any remaining content should be passed to the next parser
  * in the chain.
  *
  * @param Text $content
  * @param Container $target
  * @return void
  */
 public function parseBlock(Text $content, Container $target)
 {
     $tags = implode('|', $this->getValidTags());
     $content->handle('{
             ^                        # starts at the beginning or with a newline
             [ ]{0,3}                 # up to 3 leading spaces allowed
             (?:                      # start with one of the following...
                 \\<(?:' . $tags . ')\\s*/?\\>?|    # an opening HTML tag, or
                 \\</(?:' . $tags . ')\\s*\\>|      # a closing HTML tag, or
                 \\<!--(-(?!-)|[^\\-])*?--\\>|  # a HTML comment, or
                 \\<\\?.*?\\?\\>|                # a processing instruction, or
                 \\<![A-Z]+\\s+[^>]+\\>|        # an element type declaration, or
                 \\<!\\[CDATA\\[.*?\\]\\]\\>       # a CDATA section
             )
             .*?                      # match everything until...
             ((?=\\n[ ]*\\n)|\\Z)      # we encounter an empty line or the end
         }imsx', function (Text $content) use($target) {
         $block = new HTMLBlock($content);
         $target->addChild($block);
     }, function (Text $part) use($target) {
         $this->next->parseBlock($part, $target);
     });
 }
Exemplo n.º 15
0
 protected function parseReferenceLink(Text $content, InlineNodeAcceptorInterface $target)
 {
     $references = implode('|', array_map(function ($reference) {
         return str_replace(' ', '[ ]', preg_quote($reference));
     }, $this->context->getReferences()));
     $content->handle('/
             (?<!\\\\)
             (?|
                 ()
                 \\[
                     (' . $references . ')
                 \\]
                 [\\n\\ ]*
                 \\[\\]
               |
                 (?:
                     \\[
                         (' . $this->getNestedBrackets() . ')    # link text = $1
                     \\]
                     [ \\t\\n]*
                 )?
                 \\[
                     (' . $references . ')                       # label = $2
                 \\]
             )
         /iux', function (Text $whole, Text $linkText, Text $label) use($target) {
         $reference = $label->copy()->lower();
         $url = $this->context->getReferenceUrl($reference);
         $title = $this->context->getReferenceTitle($reference);
         if ($linkText->isEmpty()) {
             $linkText = $label;
         }
         $link = new Link($url, $linkText, $title);
         $target->addInline($link);
         $this->context->queue($link->getContent(), $link);
     }, function (Text $part) use($target) {
         $this->next->parseInline($part, $target);
     });
 }
Exemplo n.º 16
0
 protected function parseReferenceImage(Text $content, InlineNodeAcceptorInterface $target)
 {
     $references = implode('|', array_map(function ($reference) {
         return str_replace(' ', '[ ]', preg_quote($reference));
     }, $this->context->getReferences()));
     $content->handle('{
             (?<!\\\\)!
             (?|
                 ()
                 \\[
                     (' . $references . ')
                 \\]
                 [\\n\\ ]*
                 \\[\\]
               |
                 (?:
                     \\[
                         (.*?)       # alt text = $1
                     \\]
                     [ \\t\\n]*
                 )?
                 \\[
                     (' . $references . ')
                 \\]
             )
         }iux', function (Text $whole, Text $alt, Text $label) use($target) {
         $reference = $label->copy()->lower();
         $url = $this->context->getReferenceUrl($reference);
         $title = $this->context->getReferenceTitle($reference);
         if ($alt->isEmpty()) {
             $alt = $label;
         }
         $image = new Image($url, $alt, $title);
         $target->addInline($image);
     }, function (Text $part) use($target) {
         $this->next->parseInline($part, $target);
     });
 }
Exemplo n.º 17
0
 protected function parseOrderedLists(Text $content, Container $target)
 {
     $content->handle('{
             ^
             ([ ]{0,3})              # $1 - initial indent
             ([0-9]+)([.)])          # $2 - list marker; $3 - punctuation
             (?|
                 ([ ]{1,4})          # $4 - list indent
                 [^ ].*
               |
                 ()                  # ... which can also be empty
             )
             (
                 \\n\\n?
                 \\1[ ]{2}\\4
                 .*
             )*
             (
                 (?:
                     \\n
                     \\1[0-9]+\\3\\4
                     [^ ].*
                   |                     # empty items
                     \\n
                     \\1\\2
                   |                     # Lazy continuation lines
                     \\n
                     [ ]*
                     (?:
                         [^0-9>\\-+*=\\ \\n]
                       |
                         [0-9]+[^.)]
                     )
                     [^\\n]*
                 )
                 (
                     \\n\\n?
                     \\1[ ]{2}\\4
                     .*
                 )*
             )*
             $
         }mx', function (Text $content, Text $i, Text $start, Text $punctuation, Text $indent) use($target) {
         $isTerse = !$content->match('/^[ ]*$/m');
         $lines = explode("\n", $content->getString());
         $start = $start->getString();
         $indentLength = $i->getLength() + $indent->getLength() + 2;
         $list = new ListBlock('ol', $isTerse, $start);
         // Go through all the lines to assemble the list items
         $curItem = substr(array_shift($lines), $indentLength) . "\n";
         foreach ($lines as $line) {
             if (preg_match('/^[0-9]+' . preg_quote($punctuation) . '/', $line)) {
                 $this->addItemToList($curItem, $list);
                 $curItem = substr($line, $indentLength) . "\n";
             } else {
                 $curItem .= $this->unindentLine($line, $indentLength) . "\n";
             }
         }
         $this->addItemToList($curItem, $list);
         $target->addChild($list);
     }, function (Text $part) use($target) {
         $this->next->parseBlock($part, $target);
     });
 }
Exemplo n.º 18
0
 public function handleBackslashes(Text $content)
 {
     $content->replaceString(['\\\\', '\\!', '\\"', '\\#', '\\$', '\\%', '\\&', '\\\'', '\\(', '\\)', '\\*', '\\+', '\\,', '\\-', '\\.', '\\/', '\\:', '\\;', '\\<', '\\=', '\\>', '\\?', '\\@', '\\[', '\\]', '\\^', '\\_', '\\`', '\\{', '\\|', '\\}', '\\~'], ['\\', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~']);
 }
Exemplo n.º 19
0
 public function visitHardBreak(HardBreak $softBreak)
 {
     $this->buffer->append(Tag::inline('br'))->append("\n");
 }
Exemplo n.º 20
0
 public function hasLanguage()
 {
     return !$this->language->isEmpty();
 }
Exemplo n.º 21
0
 /**
  * @return Text
  */
 public function getContent()
 {
     return $this->content->copy();
 }
Exemplo n.º 22
0
 protected function handle(Text $content, $mark, Container $target, callable $next)
 {
     $content->handle($this->getPattern($mark), function () use($target) {
         $target->addChild(new HorizontalRule());
     }, $next);
 }
Exemplo n.º 23
0
 protected function makeLines(Text $text)
 {
     return $text->trim()->split('/\\n/')->apply(function (Text $line) {
         return $line->copy()->ltrim();
     });
 }