File: /www/wwwroot/aiwellbore.com/wp-content/plugins/wp-githuber-md/src/Modules/MarkdownExtraParser.php
<?php
/**
* Module Name: MarkdownExtraParser
* Module Description: Parse Markdown plaintext into HTML plaintext.
*
* @author Terry Lin
* @link https://terryl.in/
*
* @package Githuber
* @since 1.7.0
* @version 1.11.2
*/
namespace Githuber\Module;
use Markdown;
use ParsedownExtra;
/**
* Markdown Extra Parser.
*/
class MarkdownExtraParser extends ParsedownExtra {
/**
* Stores shortcodes we remove and then replace
*
* @var array
*/
protected $preserve_text_hash = array();
/**
* Preserve shortcodes, untouched by Markdown.
* This requires use within a WordPress installation.
*
* @var boolean
*/
public $preserve_shortcodes = true;
/**
* Preserve single-line <code> blocks.
*
* @var boolean
*/
public $preserve_inline_code_blocks = true;
/**
* Constructer.
*/
public function __construct() {
parent::__construct();
$is_html5_figure = githuber_get_option( 'support_html_figure', 'githuber_extensions' );
if ( 'no' !== $is_html5_figure ) {
// @phpcs:ignore
$this->InlineTypes['%'] = array( 'Figure' );
// @phpcs:ignore
$this->inlineMarkerList = '!%"*_&[:<>`~\\';
}
$is_allow_shortcode = githuber_get_option( 'allow_shortcode', 'githuber_preferences' );
if ( 'no' === $is_allow_shortcode ) {
$this->preserve_shortcodes = false;
}
}
/**
* Remove bare <p> elements. <p>s with attributes will be preserved.
*
* @param string $text HTML content.
* @return string <p>-less content.
*/
public function remove_bare_p_tags( $text ) {
return preg_replace( "#<p>(.*?)</p>(\n|$)#ums", '$1$2', $text );
}
/**
* Teansform Markdown to HTML.
*
* @param string $text Markdown content.
* @return string
*/
public function transform( $text ) {
// Preserve anything inside a single-line <code> element
if ( $this->preserve_inline_code_blocks ) {
$text = $this->single_line_code_preserve( $text );
}
// Remove all shortcodes so their interiors are left intact
if ( $this->preserve_shortcodes ) {
$text = $this->shortcode_preserve( $text );
}
$parsed_content = $this->text( $text );
$parsed_content = $this->do_restore( $parsed_content );
return $parsed_content;
}
/**
* Extend ParseDown for HTML 5 figure tag.
*
* @param array $excerpt The excerpt.
*
* @return array
*/
protected function inlineFigure( $excerpt ) {
if ( ! isset( $excerpt['text'][1] ) || '[' !== $excerpt['text'][1] ) {
return;
}
$excerpt['text'] = substr( $excerpt['text'], 1 );
$link = $this->inlineLink( $excerpt );
if ( null === $link ) {
return;
}
$attr_href = $link['element']['attributes']['href'];
$attr_text = $link['element']['text'];
$attr_title = $link['element']['attributes']['title'];
$markup = '<figure>';
$markup .= '<img src="' . $attr_href . '" alt="' . $attr_text . '">';
if ( ! empty( $attr_title ) ) {
$markup .= '<figcaption>' . $attr_title . '</figcaption>';
}
$markup .= '</figure>';
$inline = array(
'extent' => $link['extent'] + 1,
'markup' => $markup,
'element' => array(
'name' => 'img',
'attributes' => array(
'src' => $attr_href,
'alt' => $attr_text,
),
),
);
return $inline;
}
/**
* The below methods are from Jetpack: Markdown modular
*
* @link https://github.com/Automattic/jetpack/blob/master/_inc/lib/markdown/gfm.php
* @license GPL
*/
/**
* Retrieve the shortcode regular expression for searching.
*
* @return string A regex for grabbing shortcodes.
*/
protected function get_shortcode_regex() {
$pattern = get_shortcode_regex();
// don't match markdown link anchors that could be mistaken for shortcodes.
$pattern .= '(?!\()';
return "/$pattern/s";
}
/**
* Called to preserve WP shortcodes from being formatted by Markdown in any way.
*
* @param string $text Text in which to preserve shortcodes
* @return string Text with shortcodes replaced by a hash that will be restored later
*/
protected function shortcode_preserve( $text ) {
$text = preg_replace_callback( $this->get_shortcode_regex(), array( $this, 'do_remove_text' ), $text );
return $text;
}
/**
* Regex callback for text preservation
*
* @param array $m Regex $matches array
* @return string A placeholder that will later be replaced by the original text
*/
protected function do_remove_text( $m ) {
return $this->hash_block( $m[0] );
}
/**
* Call this to store a text block for later restoration.
*
* @param string $text Text to preserve for later
* @return string Placeholder that will be swapped out later for the original text
*/
protected function hash_block( $text ) {
$hash = md5( $text );
$this->preserve_text_hash[ $hash ] = $text;
$placeholder = $this->hash_maker( $hash );
return $placeholder;
}
/**
* Preserve inline code block contents by HTML encoding them. Useful before getting to KSES stripping.
*
* @param string $text Text that may need preserving
* @return string Text that was preserved if needed
*/
public function single_line_code_preserve( $text ) {
return preg_replace_callback(
"/[`]{1}([^\n`]*?[^\n`])[`]{1}/",
array( $this, 'do_single_line_code_preserve' ),
$text
);
}
/**
* Regex callback for inline code presevation
*
* @param array $matches Regex matches
* @return string Codeblock with escaped interior
*/
public function do_single_line_code_preserve( $matches ) {
if ( 'yes' === githuber_get_option( 'support_inline_code_keyboard_style', 'githuber_extensions' ) ) {
$first_char = substr( $matches[1], 0, 1 );
$last_char = substr( $matches[1], -1 );
if ( '{' === $first_char && '}' === $last_char ) {
return '<code class="kb-btn">' . $this->hash_block( esc_html( substr( $matches[1], 1, -1 ) ) ) . '</code>';
}
}
return '<code>' . $this->hash_block( esc_html( $matches[1] ) ) . '</code>';
}
/**
* Preserve code block contents by HTML encoding them. Useful before getting to KSES stripping.
*
* @param string $text Markdown/HTML content
* @return string Markdown/HTML content with escaped code blocks
*/
public function codeblock_preserve( $text ) {
return preg_replace_callback( "/^(\t*[`~]{3})([^`\n]+)?\n([\s\S]*?)\n(\\1)/m", array( $this, 'do_codeblock_preserve' ), $text );
}
/**
* Regex callback for code block preservation.
*
* @param array $matches Regex matches
* @return string Codeblock with escaped interior
*/
public function do_codeblock_preserve( $matches ) {
$block = stripslashes( $matches[3] );
// Issue #209
$block = str_replace( '&#', '_!_!_', $block );
// check `
$block = str_replace( '`', '`', $block );
$block = esc_html( $block );
$block = str_replace( '\\', '\\\\', $block );
$open = $matches[1] . $matches[2] . "\n";
$end = "\n" . $matches[4];
return $open . $block . $end;
}
/**
* Restore previously preserved (i.e. escaped) code block contents.
*
* @param string $text Markdown/HTML content with escaped code blocks
* @return string Markdown/HTML content
*/
public function codeblock_restore( $text ) {
return preg_replace_callback( "/^(\t*[`~]{3})([^`\n]+)?\n([\s\S]*?)\n(\\1)/m", array( $this, 'do_codeblock_restore' ), $text );
}
/**
* Regex callback for code block restoration (unescaping).
*
* @param array $matches Regex matches
* @return string Codeblock with unescaped interior
*/
public function do_codeblock_restore( $matches ) {
$block = html_entity_decode( $matches[3], ENT_QUOTES );
// Issue #209
$block = str_replace( '_!_!_', '&#', $block );
$block = str_replace( '`', '`', $block );
$open = $matches[1] . $matches[2] . "\n";
$end = "\n" . $matches[4];
return $open . $block . $end;
}
/**
* Restores any text preserved by $this->hash_block()
*
* @param string $text Text that may have hashed preservation placeholders
* @return string Text with hashed preseravtion placeholders replaced by original text
*/
protected function do_restore( $text ) {
// Reverse hashes to ensure nested blocks are restored.
$hashes = array_reverse( $this->preserve_text_hash, true );
foreach ( $hashes as $hash => $value ) {
$placeholder = $this->hash_maker( $hash );
$text = str_replace( $placeholder, $value, $text );
}
// reset the hash
$this->preserve_text_hash = array();
// Restore "`"
$text = str_replace( '`', '`', $text );
return $text;
}
/**
* Less glamorous than the Keymaker
*
* @param string $hash An md5 hash
* @return string A placeholder hash
*/
protected function hash_maker( $hash ) {
return 'MARKDOWN_HASH' . $hash . 'MARKDOWN_HASH';
}
}