/**
  * @param   array   $matches    A set of results of the `transform` function
  * @return  string
  */
 protected function _email_callback($matches)
 {
     $address = $matches[1];
     Kernel::addConfig('urls', $address);
     $block = Kernel::get('OutputFormatBag')->buildTag('link', $address, array('email' => $address));
     return parent::hashPart($block);
 }
 /**
  * Build each maths span
  *
  * @param   string   $texblock
  * @return  string
  */
 public function span($texblock)
 {
     $texblock = trim($texblock);
     $block = Kernel::get('OutputFormatBag')->buildTag('maths_span', $texblock, array());
     return parent::hashPart($block);
 }
 /**
  * Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
  *
  * *   $indent is the number of space to be ignored when checking for code
  *     blocks. This is important because if we don't take the indent into
  *     account, something like this (which looks right) won't work as expected:
  *
  *     <div>
  *         <div markdown="1">
  *         Hello World.  <-- Is this a Markdown code block or text?
  *         </div>  <-- Is this a Markdown code block or a real tag?
  *     <div>
  *
  *     If you don't like this, just don't indent the tag on which
  *     you apply the markdown="1" attribute.
  *
  * *   If $enclosing_tag_re is not empty, stops at the first unmatched closing
  *     tag with that name. Nested tags supported.
  *
  * *   If $span is true, text inside must treated as span. So any double
  *     newline will be replaced by a single newline so that it does not create
  *     paragraphs.
  *
  * Returns an array of that form: ( processed text , remaining text )
  *
  * @param   string  $text       The text to be parsed
  * @param   int     $indent     The indentation to use
  * @param   string  $enclosing_tag_re   The closing tag to use
  * @param   bool    $span       Are we in a span element (false by default)
  * @return  array               ( processed text , remaining text )
  */
 protected function _hashBlocks_inMarkdown($text, $indent = 0, $enclosing_tag_re = '', $span = false)
 {
     if ($text === '') {
         return array('', '');
     }
     // Regex to check for the presense of newlines around a block tag.
     $newline_before_re = '/(?:^\\n?|\\n\\n)*$/';
     $newline_after_re = '{
             ^                             # Start of text following the tag.
             (?>[ ]*<!--.*?-->)?           # Optional comment.
             [ ]*\\n                        # Must be followed by newline.
         }xs';
     // Regex to match any tag.
     $block_tag_re = '{
             (                               # $2: Capture hole tag.
                 </?                         # Any opening or closing tag.
                     (?>                     # Tag name.
                         ' . $this->block_tags_re . '            |
                         ' . $this->blocks_tags_re . '    |
                         ' . $this->clean_tags_re . '            |
                         (?!\\s)' . $enclosing_tag_re . '
                     )
                     (?:
                         (?=[\\s"\'/a-zA-Z0-9])   # Allowed characters after tag name.
                         (?>
                             ".*?"       |       # Double quotes (can contain `>`)
                             \'.*?\'     |       # Single quotes (can contain `>`)
                             .+?                 # Anything but quotes and `>`.
                         )*?
                     )?
                 >                               # End of tag.
             |
                 <!--    .*?     -->         # HTML Comment
             |
                 <\\?.*?\\?> | <%.*?%>         # Processing instruction
             |
                 <!\\[CDATA\\[.*?\\]\\]>         # CData Block
             |
                                             # Code span marker
                 `+
             ' . (!$span ? '                 # If not in span.
             |
                                             # Indented code block
                 (?: ^[ ]*\\n | ^ | \\n[ ]*\\n )
                 [ ]{' . ($indent + 4) . '}[^\\n]* \\n
                 (?>
                     (?: [ ]{' . ($indent + 4) . '}[^\\n]* | [ ]* ) \\n
                 )*
             |
                                             # Fenced code block marker
                 (?> ^ | \\n )
                 [ ]{0,' . $indent . '}~~~+[ ]*\\n
             ' : '') . '                     # End (if not is span).
             )
         }xs';
     $depth = 0;
     // Current depth inside the tag tree.
     $parsed = "";
     // Parsed text that will be returned.
     // Loop through every tag until we find the closing tag of the parent
     // or loop until reaching the end of text if no parent tag specified.
     do {
         // Split the text using the first $tag_match pattern found.
         // Text before  pattern will be first in the array, text after
         // pattern will be at the end, and between will be any catches made
         // by the pattern.
         $parts = preg_split($block_tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
         // If in Markdown span mode, add a empty-string span-level hash
         // after each newline to prevent triggering any block element.
         if ($span) {
             $void = parent::hashPart("", ':');
             $newline = "{$void}\n";
             $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
         }
         $parsed .= $parts[0];
         // Text before current tag.
         // If end of $text has been reached. Stop loop.
         if (count($parts) < 3) {
             $text = "";
             break;
         }
         $tag = $parts[1];
         // Tag to handle.
         $text = $parts[2];
         // Remaining text after current tag.
         $tag_re = preg_quote($tag);
         // For use in a regular expression.
         // Check for: Code span marker
         if ($tag[0] == "`") {
             // Find corresponding end marker.
             $tag_re = preg_quote($tag);
             // End marker found: pass text unchanged until marker.
             if (preg_match('{^(?>.+?|\\n(?!\\n))*?(?<!`)' . $tag_re . '(?!`)}', $text, $matches)) {
                 $parsed .= $tag . $matches[0];
                 $text = substr($text, strlen($matches[0]));
                 // Unmatched marker: just skip it.
             } else {
                 $parsed .= $tag;
             }
         } elseif (preg_match('{^\\n?[ ]{0,' . ($indent + 3) . '}~}', $tag)) {
             // Fenced code block marker: find matching end marker.
             $tag_re = preg_quote(trim($tag));
             // End marker found: pass text unchanged until marker.
             if (preg_match('{^(?>.*\\n)+?[ ]{0,' . $indent . '}' . $tag_re . '[ ]*\\n}', $text, $matches)) {
                 $parsed .= $tag . $matches[0];
                 $text = substr($text, strlen($matches[0]));
                 // No end marker: just skip it.
             } else {
                 $parsed .= $tag;
             }
         } elseif ($tag[0] == "\n" || $tag[0] == " ") {
             // Indented code block: pass it unchanged, will be handled later.
             $parsed .= $tag;
         } elseif (preg_match('{^<(?:' . $this->block_tags_re . ')\\b}', $tag) || preg_match('{^<(?:' . $this->blocks_tags_re . ')\\b}', $tag) && preg_match($newline_before_re, $parsed) && preg_match($newline_after_re, $text)) {
             // Need to parse tag and following text using the HTML parser.
             list($block_text, $text) = self::_hashBlocks_inHTML($tag . $text, "hashBlock", true);
             // Make sure it stays outside of any paragraph by adding newlines.
             $parsed .= "\n\n{$block_text}\n\n";
         } elseif (preg_match('{^<(?:' . $this->clean_tags_re . ')\\b}', $tag) || $tag[1] == '!' || $tag[1] == '?') {
             // Need to parse tag and following text using the HTML parser.
             // (don't check for markdown attribute)
             list($block_text, $text) = $this->_hashBlocks_inHTML($tag . $text, "hashClean", false);
             $parsed .= $block_text;
         } elseif ($enclosing_tag_re !== '' && preg_match('{^</?(?:' . $enclosing_tag_re . ')\\b}', $tag)) {
             // Increase/decrease nested tag count.
             if ($tag[1] == '/') {
                 $depth--;
             } elseif ($tag[strlen($tag) - 2] != '/') {
                 $depth++;
             }
             if ($depth < 0) {
                 // Going out of parent element. Clean up and break so we
                 // return to the calling function.
                 $text = $tag . $text;
                 break;
             }
             $parsed .= $tag;
         } else {
             $parsed .= $tag;
         }
     } while ($depth >= 0);
     return array($parsed, $text);
 }
 /**
  * @param   array   $matches    A set of results of the `transform` function
  * @return  string
  */
 protected function _inline_callback($matches)
 {
     $alt_text = $matches[2];
     $url = $matches[3] == '' ? $matches[4] : $matches[3];
     $title =& $matches[7];
     $attributes = array();
     $attributes['alt'] = Lexer::runGamut(GamutLoader::TOOL_ALIAS . ':EncodeAttribute', $alt_text);
     $attributes['src'] = Lexer::runGamut(GamutLoader::TOOL_ALIAS . ':EncodeAttribute', $url);
     if (!empty($title)) {
         $attributes['title'] = Lexer::runGamut(GamutLoader::TOOL_ALIAS . ':EncodeAttribute', $title);
     }
     $block = Kernel::get('OutputFormatBag')->buildTag('image', null, $attributes);
     return parent::hashPart($block);
 }
 /**
  * @param   array   $matches    A set of results of the `transform()` function
  * @return  string
  */
 protected function _callback($matches)
 {
     return parent::hashPart(Kernel::get('OutputFormatBag')->buildTag('new_line') . "\n");
 }
 /**
  * Create a code span markup for $code. Called from handleSpanToken.
  *
  * @param   string  $code
  * @return  string
  */
 public function span($code)
 {
     $codeblock = Kernel::get('OutputFormatBag')->buildTag('code', Helper::escapeCodeContent(trim($code)));
     return parent::hashPart($codeblock);
 }
 /**
  * Handle $token provided by parseSpan by determining its nature and
  * returning the corresponding value that should replace it.
  *
  * @param   string  $token
  * @param   string  $str
  * @return  string
  */
 public function handleSpanToken($token, &$str)
 {
     switch ($token[0]) {
         case "\\":
             if ($token[1] == "(") {
                 $texend = strpos($str, '\\)');
                 if ($texend) {
                     $eqn = substr($str, 0, $texend);
                     $str = substr($str, $texend + 2);
                     $texspan = Lexer::runGamut('filter:Maths:span', $eqn);
                     return parent::hashPart($texspan);
                 } else {
                     return $str;
                 }
             } else {
                 return parent::hashPart("&#" . ord($token[1]) . ";");
             }
         case "`":
             // Search for end marker in remaining text.
             if (preg_match('/^(.*?[^`])' . preg_quote($token) . '(?!`)(.*)$/sm', $str, $matches)) {
                 $str = $matches[2];
                 $codespan = Lexer::runGamut('filter:CodeBlock:span', $matches[1], true);
                 return parent::hashPart($codespan);
             }
             return $token;
             // return as text since no ending marker found.
         // return as text since no ending marker found.
         default:
             return parent::hashPart($token);
     }
 }
 /**
  * @param   string  $text
  * @return  string
  */
 public function transform($text)
 {
     $token_stack = array('');
     $text_stack = array('');
     $italic = '';
     $strong = '';
     $tree_char_em = false;
     while (1) {
         // Get prepared regular expression for seraching emphasis tokens in current context.
         $token_re = self::$em_strong_prepared["{$italic}{$strong}"];
         // Each loop iteration search for the next emphasis token.
         // Each token is then passed to handleSpanToken.
         $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
         $text_stack[0] .= $parts[0];
         $token =& $parts[1];
         $text =& $parts[2];
         if (empty($token)) {
             // Reached end of text span: empty stack without emitting any more emphasis.
             while ($token_stack[0]) {
                 $text_stack[1] .= array_shift($token_stack);
                 $text_stack[0] .= array_shift($text_stack);
             }
             break;
         }
         $token_len = strlen($token);
         if ($tree_char_em) {
             // Reached closing marker while inside a three-char emphasis.
             if ($token_len == 3) {
                 // Three-char closing marker, close em and strong.
                 array_shift($token_stack);
                 $span = Lexer::runGamut('span_gamut', array_shift($text_stack));
                 $span = Kernel::get('OutputFormatBag')->buildTag('italic', $span);
                 $span = Kernel::get('OutputFormatBag')->buildTag('bold', $span);
                 $text_stack[0] .= parent::hashPart($span);
                 $italic = '';
                 $strong = '';
             } else {
                 // Other closing marker: close one em or strong and
                 // change current token state to match the other
                 $token_stack[0] = str_repeat($token[0], 3 - $token_len);
                 $tag = $token_len == 2 ? "bold" : "italic";
                 $span = Lexer::runGamut('span_gamut', $text_stack[0]);
                 $span = Kernel::get('OutputFormatBag')->buildTag($tag, $span);
                 $text_stack[0] = parent::hashPart($span);
                 ${$tag} = '';
                 // $$tag stands for $italic or $strong
             }
             $tree_char_em = false;
         } elseif ($token_len == 3) {
             if ($italic) {
                 // Reached closing marker for both em and strong.
                 // Closing strong marker:
                 for ($i = 0; $i < 2; ++$i) {
                     $shifted_token = array_shift($token_stack);
                     $tag = strlen($shifted_token) == 2 ? "bold" : "italic";
                     $span = Lexer::runGamut('span_gamut', array_shift($text_stack));
                     $span = Kernel::get('OutputFormatBag')->buildTag($tag, $span);
                     $text_stack[0] .= parent::hashPart($span);
                     ${$tag} = '';
                     // $$tag stands for $italic or $strong
                 }
             } else {
                 // Reached opening three-char emphasis marker. Push on token
                 // stack; will be handled by the special condition above.
                 $italic = $token[0];
                 $strong = "{$italic}{$italic}";
                 array_unshift($token_stack, $token);
                 array_unshift($text_stack, '');
                 $tree_char_em = true;
             }
         } elseif ($token_len == 2) {
             if ($strong) {
                 // Unwind any dangling emphasis marker:
                 if (strlen($token_stack[0]) == 1) {
                     $text_stack[1] .= array_shift($token_stack);
                     $text_stack[0] .= array_shift($text_stack);
                 }
                 // Closing strong marker:
                 array_shift($token_stack);
                 $span = Lexer::runGamut('span_gamut', array_shift($text_stack));
                 $span = Kernel::get('OutputFormatBag')->buildTag('bold', $span);
                 $text_stack[0] .= parent::hashPart($span);
                 $strong = '';
             } else {
                 array_unshift($token_stack, $token);
                 array_unshift($text_stack, '');
                 $strong = $token;
             }
         } else {
             // Here $token_len == 1
             if ($italic) {
                 if (strlen($token_stack[0]) == 1) {
                     // Closing emphasis marker:
                     array_shift($token_stack);
                     $span = Lexer::runGamut('span_gamut', array_shift($text_stack));
                     $span = Kernel::get('OutputFormatBag')->buildTag('italic', $span);
                     $text_stack[0] .= parent::hashPart($span);
                     $italic = '';
                 } else {
                     $text_stack[0] .= $token;
                 }
             } else {
                 array_unshift($token_stack, $token);
                 array_unshift($text_stack, '');
                 $italic = $token;
             }
         }
     }
     return $text_stack[0];
 }