Your IP : 216.73.217.176


Current Path : /home/bechata/mp/wp-content/uploads/2022/ejn73c/
Upload File :
Current File : /home/bechata/mp/wp-content/uploads/2022/ejn73c/src.tar

Githuber.php000066600000030302151747671010007041 0ustar00<?php
/**
 * Class Githuber
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.13.1
 */

use Githuber\Controller as Controller;
use Githuber\Module as Module;
use Githuber\Controller\Monolog as Monolog;

class Githuber {

	public $current_url;

	/**
	 * Constructer.
	 */
	public function __construct() {

		add_action( 'init', array( $this, 'load_textdomain' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_styles' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );

		if ( ! isset( $_SERVER['HTTP_HOST'] ) || ! isset( $_SERVER['REQUEST_URI'] ) ) {
			$_SERVER['HTTP_HOST']   = '127.0.0.1';
			$_SERVER['REQUEST_URI'] = '/';
		}

		$this->current_url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

		// Only use it in DEBUG mode.
		githuber_logger( 'Hello, Githuber MD.', array(
			'wp_version'  => $GLOBALS['wp_version'],
			'php_version' => phpversion(),
		) );

		// If in Admin Panel and WordPress > 5.0, load Class editor and disable Gutenberg editor.
		if ( $GLOBALS['wp_version'] > '5.0' && is_admin() ) {
			add_filter('use_block_editor_for_post', '__return_false', 5);
		}

		// Load TOC widget. // 
		if ( 'yes' == githuber_get_option( 'support_toc', 'githuber_modules' ) ) {
			if ( 'yes' == githuber_get_option( 'is_toc_widget', 'githuber_modules' ) ) {
				add_action( 'widgets_init', function() {
					register_widget( 'Githuber_Widget_Toc' );
				} );
			}
		}

		// Load core functions when `wp_loaded` is ready.
		add_action( 'wp_loaded', array( $this, 'init' ) );

		// Only use it in DEBUG mode.
		githuber_logger( 'Hook: wp_loaded', array( 'url' => $this->current_url ) );
	}

	/**
	 * Initialize everything the Githuber plugin needs.
	 */
	public function init() {

		// Only load controllers in backend.
		if ( is_admin() ) {

			$register = new Controller\Register();
			$register->init();

			$setting = new Controller\Setting();
			$setting->init();
	
			if ( 'yes' === githuber_get_option( 'support_image_paste', 'githuber_modules' ) ) {
				$image_paste = new Controller\ImagePaste();
				$image_paste->init();
			}

			if ( 'yes' === githuber_get_option( 'editor_html_decode', 'githuber_markdown' ) ) {
				$customMediaLibrary = new Controller\CustomMediaLibrary();
				$customMediaLibrary->init();
			}

			if ( 'yes' === githuber_get_option( 'editor_spell_check', 'githuber_markdown' ) ) {
				$spellCheck = new Controller\SpellCheck();
				$spellCheck->init();
			}

			if ( 'yes' === githuber_get_option( 'keyword_suggestion_tool', 'githuber_markdown' ) ) {
				$keywordSuggestion = new Controller\KeywordSuggestion();
				$keywordSuggestion->init();
			}

			$markdown = new Controller\Markdown();
			$markdown->init();
		}

		/**
		 * Let's start loading frontend modules.
		 */ 

		// Module Name: FlowChart
		if ( 'yes' === githuber_get_option( 'support_flowchart', 'githuber_modules' ) ) {
			$module_flowchart = new Module\FlowChart();
			$module_flowchart->init();
		}

		// Module Name: KaTeX
		if ( 'yes' === githuber_get_option( 'support_katex', 'githuber_modules' ) ) {
			$module_katex = new Module\KaTeX();
			$module_katex->init();
		}

		// Module Name: Sequence Diagram
		if ( 'yes' === githuber_get_option( 'support_sequence_diagram', 'githuber_modules' ) ) {
			$module_sequence = new Module\SequenceDiagram();
			$module_sequence->init();
		}

		// Module Name: Mermaid
		if ( 'yes' === githuber_get_option( 'support_mermaid', 'githuber_modules' ) ) {
			$module_mermaid = new Module\Mermaid();
			$module_mermaid->init();
		}

		// Module Name: Prism
		if ( 'yes' === githuber_get_option( 'support_prism', 'githuber_modules' ) ) {
			$module_prism = new Module\Prism();
			$module_prism->init();
		}

		// Module Name: Highlight
		if ( 'yes' === githuber_get_option( 'support_highlight', 'githuber_modules' ) ) {
			$module_highlight = new Module\Highlight();
			$module_highlight->init();
		}

		// Module Name: MathJax
		if ( 'yes' === githuber_get_option( 'support_mathjax', 'githuber_modules' ) ) {
			$module_mathjax = new Module\MathJax();
			$module_mathjax->init();
		}

		// Replace `&amp;` to `&` in URLs in post content.
		if ( 'yes' == githuber_get_option( 'support_toc', 'githuber_modules' ) ) {
			$module_toc = new Module\Toc();
			$module_toc->init();
		}

		// Copy to Clipboard
		if ( 'yes' === githuber_get_option( 'support_clipboard', 'githuber_modules' ) ) {
			$module_clipboard = new Module\Clipboard();
			$module_clipboard->init();
		}

		// Emojify
		if ( 'yes' === githuber_get_option( 'support_emojify', 'githuber_modules' ) ) {
			$module_emojify = new Module\Emojify();
			$module_emojify->init();
		}

		/**
		 * Let's start setting user's perferences...
		 */

		if ( 'yes' !== githuber_get_option( 'smart_quotes', 'githuber_preferences' ) ) {
			remove_filter( 'the_content', 'wptexturize' );
		}

		// Replace `&amp;` to `&` in URLs in post content.
		if ( 'yes' === githuber_get_option( 'restore_ampersands', 'githuber_preferences' ) ) {
			add_filter( 'the_content', function( $string ) {
				return preg_replace_callback( '|<a\b([^>]*)>(.*?)</a>|', function( $matches ) {
					return '<a' . str_replace( '&amp;', '&', $matches[1] ) . '>' . $matches[2] . '</a>';
				}, $string );
			}, 10, 1 );
		}
	}

	/**
	 * Load plugin textdomain.
	 */
	public function load_textdomain() {
		load_plugin_textdomain( GITHUBER_PLUGIN_TEXT_DOMAIN, false, GITHUBER_PLUGIN_LANGUAGE_PACK ); 
	}

	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {
		wp_register_style( 'md-style', false );
		wp_enqueue_style( 'md-style' );
		wp_add_inline_style( 'md-style', $this->get_front_enqueue_styles() );
	}

	public function get_front_enqueue_styles() {

		$custom_css = '';

		if ( 'yes' === githuber_get_option( 'support_task_list', 'githuber_extensions' ) ) {
	
			$custom_css .= '
				.gfm-task-list {
					border: 1px solid transparent;
					list-style-type: none;
				}
				.gfm-task-list input {
					margin-right: 10px !important;
				}
			';
		}

		if ( 'yes' === githuber_get_option( 'support_katex', 'githuber_modules' ) ) {
		
			$custom_css .= '
				.katex-container {
					margin: 25px !important;
					text-align: center;
				}
				.katex-container.katex-inline {
					display: inline-block !important;
					background: none !important;
					margin: 0 !important;
					padding: 0 !important;
				}
				pre .katex-container {
					font-size: 1.4em !important;
				}
				.katex-inline {
					background: none !important;
					margin: 0 3px;
				}
			';
		}

		if ( '_blank' === githuber_get_option( 'post_link_target_attribute', 'githuber_preferences' ) ) {
		  
			$custom_css .= '
				code.kb-btn {
					display: inline-block;
					color: #666;
					font: bold 9pt arial;
					text-decoration: none;
					text-align: center;
					padding: 2px 5px;
					margin: 0 5px;
					background: #eff0f2;
					-moz-border-radius: 4px;
					border-radius: 4px;
					border-top: 1px solid #f5f5f5;
					-webkit-box-shadow: inset 0 0 20px #e8e8e8, 0 1px 0 #c3c3c3, 0 1px 0 #c9c9c9, 0 1px 2px #333;
					-moz-box-shadow: inset 0 0 20px #e8e8e8, 0 1px 0 #c3c3c3, 0 1px 0 #c9c9c9, 0 1px 2px #333;
					box-shadow: inset 0 0 20px #e8e8e8, 0 1px 0 #c3c3c3, 0 1px 0 #c9c9c9, 0 1px 2px #333;
					text-shadow: 0px 1px 0px #f5f5f5;
				}
			';
		}

		if ( 'yes' === githuber_get_option( 'support_clipboard', 'githuber_modules' ) ) {

			$svg = "data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='16px' height='16px' viewBox='888 888 16 16' enable-background='new 888 888 16 16' xml:space='preserve'%3E %3Cpath fill='%23333333' d='M903.143,891.429c0.238,0,0.44,0.083,0.607,0.25c0.167,0.167,0.25,0.369,0.25,0.607v10.857 c0,0.238-0.083,0.44-0.25,0.607s-0.369,0.25-0.607,0.25h-8.571c-0.238,0-0.44-0.083-0.607-0.25s-0.25-0.369-0.25-0.607v-2.571 h-4.857c-0.238,0-0.44-0.083-0.607-0.25s-0.25-0.369-0.25-0.607v-6c0-0.238,0.06-0.5,0.179-0.786s0.262-0.512,0.428-0.679 l3.643-3.643c0.167-0.167,0.393-0.309,0.679-0.428s0.547-0.179,0.786-0.179h3.714c0.238,0,0.44,0.083,0.607,0.25 c0.166,0.167,0.25,0.369,0.25,0.607v2.929c0.404-0.238,0.785-0.357,1.143-0.357H903.143z M898.286,893.331l-2.67,2.669h2.67V893.331 z M892.571,889.902l-2.669,2.669h2.669V889.902z M894.321,895.679l2.821-2.822v-3.714h-3.428v3.714c0,0.238-0.083,0.441-0.25,0.607 s-0.369,0.25-0.607,0.25h-3.714v5.714h4.571v-2.286c0-0.238,0.06-0.5,0.179-0.786C894.012,896.071,894.155,895.845,894.321,895.679z M902.857,902.857v-10.286h-3.429v3.714c0,0.238-0.083,0.441-0.25,0.607c-0.167,0.167-0.369,0.25-0.607,0.25h-3.714v5.715H902.857z' /%3E %3C/svg%3E";
			$svg = addslashes($svg);

			$custom_css .= '
				.copy-button {
					cursor: pointer;
					border: 0;
					font-size: 12px;
					text-transform: uppercase;
					font-weight: 500;
					padding: 3px 6px 3px 6px;
					background-color: rgba(255, 255, 255, 0.6);
					position: absolute;
					overflow: hidden;
					top: 5px;
					right: 5px;
					border-radius: 3px;
				}
				.copy-button:before {
					content: "";
					display: inline-block;
					width: 16px;
					height: 16px;
					margin-right: 3px;
					background-size: contain;
					background-image: url("' . $svg . '");
					background-repeat: no-repeat;
					position: relative;
					top: 3px;
				}
				pre {
					position: relative;
				}
				pre:hover .copy-button {
					background-color: rgba(255, 255, 255, 0.9);
				}
			';
		}

		if ( 'yes' == githuber_get_option( 'support_toc', 'githuber_modules' ) ) {
			$custom_css .= '
				.md-widget-toc {
					padding: 15px;
				}
				.md-widget-toc a {
					color: #333333;
				}
				.post-toc-header {
					font-weight: 600;
					margin-bottom: 10px;
				}
				.md-post-toc {
					font-size: 0.9em;
				}
				.post h2 {
					overflow: hidden;
				}
				.post-toc-block {
					margin: 0 10px 20px 10px;
					overflow: hidden;
				}
				.post-toc-block.with-border {
					border: 1px #dddddd solid;
					padding: 10px;
				}
				.post-toc-block.float-right {
					max-width: 320px;
					float: right;
				}
				.post-toc-block.float-left {
					max-width: 320px;
					float: left;
				}
				.md-widget-toc ul, .md-widget-toc ol, .md-post-toc ul, .md-post-toc ol {
					padding-left: 15px;
					margin: 0;
				}
				.md-widget-toc ul ul, .md-widget-toc ul ol, .md-widget-toc ol ul, .md-widget-toc ol ol, .md-post-toc ul ul, .md-post-toc ul ol, .md-post-toc ol ul, .md-post-toc ol ol {
					padding-left: 2em;
				}
				.md-widget-toc ul ol, .md-post-toc ul ol {
					list-style-type: lower-roman;
				}
				.md-widget-toc ul ul ol, .md-widget-toc ul ol ol, .md-post-toc ul ul ol, .md-post-toc ul ol ol {
					list-style-type: lower-alpha;
				}
				.md-widget-toc ol ul, .md-widget-toc ol ol, .md-post-toc ol ul, .md-post-toc ol ol {
					padding-left: 2em;
				}
				.md-widget-toc ol ol, .md-post-toc ol ol {
					list-style-type: lower-roman;
				}
				.md-widget-toc ol ul ol, .md-widget-toc ol ol ol, .md-post-toc ol ul ol, .md-post-toc ol ol ol {
					list-style-type: lower-alpha;
				}
			';
		}

		if ( 'yes' == githuber_get_option( 'support_mathjax', 'githuber_modules' ) ) {
			$custom_css .= '
				.post pre code script, .language-mathjax ~ .copy-button {
					display: none !important;
				}
			';
		}

		if ( 'yes' === githuber_get_option( 'support_emojify', 'githuber_modules' ) ) {
			$emoji_size = githuber_get_option( 'emojify_emoji_size', 'githuber_modules' );

			// If not the default value, overwrite customized size to the CSS.
			if ( '1.5em' !== $emoji_size ) {
				$custom_css .= '
					.post .emoji {
						width: ' . $emoji_size . ';
						height: ' . $emoji_size . ';
					}
				';
			}
		}

		return preg_replace( '/\s+/', ' ', $custom_css );
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '';

		if ( '_blank' === githuber_get_option( 'post_link_target_attribute', 'githuber_preferences' ) ) {
			$script = '
				<script id="preference-link-target">
					(function($) {
						$(function() {
							$(".post").find("a").each(function() {
								var link_href = $(this).attr("href");
								if (link_href.indexOf("#") == -1) {
									$(this).attr("target", "_blank");
								}
							});
						});
					})(jQuery);
				</script>
			';

			$script = preg_replace( '/\s+/', ' ', $script );
		}

		echo $script;
	}
}

wp_utilities/class-settings-api.php000066600000060504151747671010013532 0ustar00<?php

/**
 * Githuber MD Settings API wrapper class
 *
 * @author Terry Lin
 * @package Githuber
 * @since 1.0.0
 * @version 1.7.0
 * @license GPLv3
 *
 * Notice:
 * This script is modified a lot by Terry L. for Githuber MD plugin use.
 * If you're looking for original code, go visit https://github.com/tareq1988/wordpress-settings-api-class
 *
 * @version 1.3 (27-Sep-2016)
 * @author Tareq Hasan <tareq@weDevs.com>
 * @link https://tareq.co Tareq Hasan
 * @license MIT
 */

class Githuber_Settings_API {

	/**
	 * settings sections array
	 *
	 * @var array
	 */
	protected $settings_sections = array();

	/**
	 * Settings fields array
	 *
	 * @var array
	 */
	protected $settings_fields = array();

	public function __construct() {
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
	}

	/**
	 * Enqueue scripts and styles
	 */
	function admin_enqueue_scripts() {
		wp_enqueue_style( 'wp-color-picker' );

		wp_enqueue_media();
		wp_enqueue_script( 'wp-color-picker' );
		wp_enqueue_script( 'jquery' );

		wp_enqueue_script( 'prettify-print', GITHUBER_PLUGIN_URL . 'assets/vendor/editor.md/lib/prettify.min.js', array( 'jquery' ), '1.0', true );
		wp_enqueue_script( 'setting-api', GITHUBER_PLUGIN_URL . 'assets/js/githuber-md-setting-api.js', array( 'jquery' ), GITHUBER_PLUGIN_VERSION, true );
	}

	/**
	 * Set settings sections
	 *
	 * @param array   $sections setting sections array
	 */
	function set_sections( $sections ) {
		$this->settings_sections = $sections;

		return $this;
	}

	/**
	 * Add a single section
	 *
	 * @param array   $section
	 */
	function add_section( $section ) {
		$this->settings_sections[] = $section;

		return $this;
	}

	/**
	 * Set settings fields
	 *
	 * @param array   $fields settings fields array
	 */
	function set_fields( $fields ) {
		$this->settings_fields = $fields;

		return $this;
	}

	function add_field( $section, $field ) {
		$defaults = array(
			'name'  => '',
			'label' => '',
			'desc'  => '',
			'type'  => 'text'
		);

		$arg = wp_parse_args( $field, $defaults );
		$this->settings_fields[$section][] = $arg;

		return $this;
	}

	/**
	 * Initialize and registers the settings sections and fileds to WordPress
	 *
	 * Usually this should be called at `admin_init` hook.
	 *
	 * This function gets the initiated settings sections and fields. Then
	 * registers them to WordPress and ready for use.
	 */
	function admin_init() {
		//register settings sections
		foreach ( $this->settings_sections as $section ) {
			if ( false == get_option( $section['id'] ) ) {
				add_option( $section['id'] );
			}

			if ( isset($section['desc']) && !empty($section['desc']) ) {
				$section['desc'] = '<div class="inside">' . $section['desc'] . '</div>';
				$callback = function() use ( $section ) {
					echo str_replace( '"', '\"', $section['desc'] );
				};
			} else if ( isset( $section['callback'] ) ) {
				$callback = $section['callback'];
			} else {
				$callback = null;
			}

			$page_title = '<span class="g-tab-title">' . $section['title'] . '</span>';

			add_settings_section( $section['id'] . '_0', $page_title, $callback, $section['id'] );
		}

		//register settings fields
		foreach ( $this->settings_fields as $section => $field ) {
			$i = 0;
			$next_section_group = $section . '_' . $i;

			foreach ( $field as $option ) {
				$i++;
				$name = isset( $option['name'] ) ? $option['name'] : 'No name';
				$type = isset( $option['type'] ) ? $option['type'] : 'text';
				$label = isset( $option['label'] ) ? $option['label'] : '';
				$callback = isset( $option['callback'] ) ? $option['callback'] : array( $this, 'callback_' . $type );
				
				if ( isset( $option['section_title'] ) && true === $option['section_title'] ) {
					// Create a section in same page if $name is empty.
					$next_section_group = $section . '_' . $i;
					$location_id        = isset( $option['location_id'] ) ? $option['location_id'] : '';
					$section_title      = '<span class="g-section-title" id="' . $location_id . '">' . $option['label'] . '</span>';

					if ( ! empty( $option['desc'] )) {
						$section_title = '<span class="g-section-title" id="' . $location_id . '">' . $option['label'] . '<span class="g-section-title-desc">' . $option['desc'] . '</span></span>';
					}
					add_settings_section( $next_section_group, $section_title, '', $section);
					
				} else {
					$args = array(
						'id'                => $name,
						'class'             => isset( $option['class'] ) ? $option['class'] : $name,
						'label_for'         => "{$section}[{$name}]",
						'desc'              => isset( $option['desc'] ) ? $option['desc'] : '',
						'name'              => $label,
						'section'           => $section,
						'size'              => isset( $option['size'] ) ? $option['size'] : null,
						'options'           => isset( $option['options'] ) ? $option['options'] : '',
						'std'               => isset( $option['default'] ) ? $option['default'] : '',
						'sanitize_callback' => isset( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : '',
						'type'              => $type,
						'placeholder'       => isset( $option['placeholder'] ) ? $option['placeholder'] : '',
						'min'               => isset( $option['min'] ) ? $option['min'] : '',
						'max'               => isset( $option['max'] ) ? $option['max'] : '',
						'step'              => isset( $option['step'] ) ? $option['step'] : '',
						'location_id'       => isset( $option['location_id'] ) ? $option['location_id'] : '',
						'parent'            => isset( $option['parent'] ) ? $option['parent'] : '',
						'has_child'         => isset( $option['has_child'] ) ? $option['has_child'] : '',
					);
	
					add_settings_field( "{$section}[{$name}]", $label, $callback, $section, $next_section_group, $args );
				}
			}
		}

		// creates our settings in the options table
		foreach ( $this->settings_sections as $section ) {
			register_setting( $section['id'], $section['id'], array( $this, 'sanitize_options' ) );
		}
	}

	/**
	 * Get field description for display
	 *
	 * @param array   $args settings field args
	 */
	public function get_field_description( $args ) {
		if ( ! empty( $args['desc'] ) ) {
			$desc = sprintf( '<p class="description">%s</p>', $args['desc'] );
		} else {
			$desc = '';
		}

		return $desc;
	}

	/**
	 * Displays a text field for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_text( $args ) {

		$value       = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$size        = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
		$type        = isset( $args['type'] ) ? $args['type'] : 'text';
		$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';

		$html        = sprintf( '<input type="%1$s" class="%2$s-text" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder );
		$html       .= $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a url field for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_url( $args ) {
		$this->callback_text( $args );
	}

	/**
	 * Displays a number field for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_number( $args ) {
		$value       = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$size        = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
		$type        = isset( $args['type'] ) ? $args['type'] : 'number';
		$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
		$min         = ( $args['min'] == '' ) ? '' : ' min="' . $args['min'] . '"';
		$max         = ( $args['max'] == '' ) ? '' : ' max="' . $args['max'] . '"';
		$step        = ( $args['step'] == '' ) ? '' : ' step="' . $args['step'] . '"';

		$html        = sprintf( '<input type="%1$s" class="%2$s-number" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder, $min, $max, $step );
		$html       .= $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a checkbox for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_checkbox( $args ) {

		$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$html  = '<fieldset>';
		$html  .= sprintf( '<label for="wpuf-%1$s-%2$s">', $args['section'], $args['id'] );
		$html  .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="off" />', $args['section'], $args['id'] );
		$html  .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s-%2$s" name="%1$s[%2$s]" value="on" %3$s />', $args['section'], $args['id'], checked( $value, 'on', false ) );
		$html  .= sprintf( '%1$s</label>', $args['desc'] );
		$html  .= '</fieldset>';

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a toggle for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_toggle( $args ) {

		$value     = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$location  = isset( $args['location_id'] ) ? $args['location_id'] : '';
		$has_child = isset( $args['has_child'] ) ? 'has-child' : '';
		$size      = isset( $args['size'] ) ? $args['size'] : '';

		$html = '';
		$html .= sprintf( '<div class="wpmd setting-toggle %4$s %5$s" data-location="%1$s" data-target="%2$s[%3$s]" data-setting="%3$s">', $location, $args['section'], $args['id'], $has_child, $size );
		$html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="no" />', $args['section'], $args['id'] );
		$html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s-%2$s" name="%1$s[%2$s]" value="yes" %3$s />', $args['section'], $args['id'], checked( $value, 'yes', false ) );
		$html .= sprintf( '<label for="wpuf-%1$s-%2$s">', $args['section'], $args['id'] );
		$html .= sprintf( '%1$s</label>', 'toggle' );
		$html .= '</div>';
		$html .= $this->get_field_description( $args );
		$html .= '</fieldset>';

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a multicheckbox for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_multicheck( $args ) {

		$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
		$html  = '<fieldset>';
		$html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="" />', $args['section'], $args['id'] );

		$option_count = count( $args['options'] );

		foreach ( $args['options'] as $key => $label ) {
			$checked = isset( $value[$key] ) ? $value[$key] : '0';

			if ( $option_count < 5 ) {
				$html .= '<div>';
			} else {
				$html .= '<div style="display: inline-block; margin-right: 15px;">';
			}
			$html    .= sprintf( '<label for="wpuf-%1$s-%2$s-%3$s">', $args['section'], $args['id'], $key );
			$html    .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s-%2$s-%3$s" name="%1$s[%2$s][%3$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $checked, $key, false ) );
			$html    .= sprintf( '%1$s</label>',  $label );
			$html .= '</div>';
		}

		$html .= $this->get_field_description( $args );
		$html .= '</fieldset>';

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a radio button for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_radio( $args ) {

		$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
		$html  = '<fieldset>';

		foreach ( $args['options'] as $key => $label ) {
			$html .= sprintf( '<label for="wpuf-%1$s-%2$s-%3$s">',  $args['section'], $args['id'], $key );
			$html .= sprintf( '<input type="radio" class="radio" id="wpuf-%1$s-%2$s-%3$s" name="%1$s[%2$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $value, $key, false ) );
			$html .= sprintf( '%1$s</label><br>', $label );
		}

		$html .= $this->get_field_description( $args );
		$html .= '</fieldset>';

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a selectbox for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_select( $args ) {

		$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$size  = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
		$html  = sprintf( '<select class="%1$s" name="%2$s[%3$s]" id="%2$s[%3$s]">', $size, $args['section'], $args['id'] );

		foreach ( $args['options'] as $key => $label ) {
			$html .= sprintf( '<option value="%s"%s>%s</option>', $key, selected( $value, $key, false ), $label );
		}

		$html .= sprintf( '</select>' );
		$html .= $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a textarea for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_textarea( $args ) {

		$value       = esc_textarea( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$size        = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
		$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="'.$args['placeholder'].'"';

		$html        = sprintf( '<textarea rows="5" cols="55" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]"%4$s>%5$s</textarea>', $size, $args['section'], $args['id'], $placeholder, $value );
		$html        .= $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays the html for a settings field
	 *
	 * @param array   $args settings field args
	 * @return string
	 */
	function callback_html( $args ) {

		$html = $args['desc'];

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a rich text textarea for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_wysiwyg( $args ) {

		$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
		$size  = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : '500px';

		if ( ! empty( $args['parent'] ) ) {
			echo '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">';
		}

		echo '<div style="max-width: ' . $size . ';">';

		$editor_settings = array(
			'teeny'         => true,
			'textarea_name' => $args['section'] . '[' . $args['id'] . ']',
			'textarea_rows' => 10
		);

		if ( isset( $args['options'] ) && is_array( $args['options'] ) ) {
			$editor_settings = array_merge( $editor_settings, $args['options'] );
		}

		wp_editor( $value, $args['section'] . '-' . $args['id'], $editor_settings );

		echo '</div>';

		echo $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			echo '</div>';
		}
	}

	/**
	 * Displays a file upload field for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_file( $args ) {

		$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$size  = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
		$id    = $args['section']  . '[' . $args['id'] . ']';
		$label = isset( $args['options']['button_label'] ) ? $args['options']['button_label'] : __( 'Choose File' );

		$html  = sprintf( '<input type="text" class="%1$s-text wpsa-url" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
		$html  .= '<input type="button" class="button wpsa-browse" value="' . $label . '" />';
		$html  .= $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a password field for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_password( $args ) {

		$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$size  = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';

		$html  = sprintf( '<input type="password" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
		$html  .= $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Displays a color picker field for a settings field
	 *
	 * @param array   $args settings field args
	 */
	function callback_color( $args ) {

		$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
		$size  = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';

		$html  = sprintf( '<input type="text" class="%1$s-text wp-color-picker-field" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" data-default-color="%5$s" />', $size, $args['section'], $args['id'], $value, $args['std'] );
		$html  .= $this->get_field_description( $args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}


	/**
	 * Displays a select box for creating the pages select box
	 *
	 * @param array   $args settings field args
	 */
	function callback_pages( $args ) {

		$dropdown_args = array(
			'selected' => esc_attr($this->get_option($args['id'], $args['section'], $args['std'] ) ),
			'name'     => $args['section'] . '[' . $args['id'] . ']',
			'id'       => $args['section'] . '[' . $args['id'] . ']',
			'echo'     => 0
		);
		$html = wp_dropdown_pages( $dropdown_args );

		if ( ! empty( $args['parent'] ) ) {
			$html = '<div class="setting-has-parent" data-parent="' . $args['parent'] . '">' . $html . '</div>';
		}

		echo $html;
	}

	/**
	 * Sanitize callback for Settings API
	 *
	 * @return mixed
	 */
	function sanitize_options( $options ) {

		if ( !$options ) {
			return $options;
		}

		foreach( $options as $option_slug => $option_value ) {
			$sanitize_callback = $this->get_sanitize_callback( $option_slug );

			// If callback is set, call it
			if ( $sanitize_callback ) {
				$options[ $option_slug ] = call_user_func( $sanitize_callback, $option_value );
				continue;
			}
		}

		return $options;
	}

	/**
	 * Get sanitization callback for given option slug
	 *
	 * @param string $slug option slug
	 *
	 * @return mixed string or bool false
	 */
	function get_sanitize_callback( $slug = '' ) {
		if ( empty( $slug ) ) {
			return false;
		}

		// Iterate over registered fields and see if we can find proper callback
		foreach( $this->settings_fields as $section => $options ) {
			foreach ( $options as $option ) {
				if ( ! isset( $option['name'] ) || $option['name'] != $slug ) {
					continue;
				}

				// Return the callback name
				return isset( $option['sanitize_callback'] ) && is_callable( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : false;
			}
		}

		return false;
	}

	/**
	 * Get the value of a settings field
	 *
	 * @param string  $option  settings field name
	 * @param string  $section the section name this field belongs to
	 * @param string  $default default text if it's not found
	 * @return string
	 */
	function get_option( $option, $section, $default = '' ) {

		$options = get_option( $section );

		if ( isset( $options[$option] ) ) {
			return $options[$option];
		}

		return $default;
	}

	/**
	 * Show navigations as tab
	 *
	 * Shows all the settings section labels as tab
	 */
	function show_navigation() {
		$html = '<h2 class="nav-tab-wrapper">';

		$count = count( $this->settings_sections );

		// don't show the navigation if only one section exists
		if ( $count === 1 ) {
			return;
		}

		foreach ( $this->settings_sections as $tab ) {
			$html .= sprintf( '<a href="#%1$s" class="nav-tab" id="%1$s-tab">%2$s</a>', $tab['id'], $tab['title'] );
		}

		$html .= '</h2>';

		echo $html;
	}

	/**
	 * Show the section settings forms
	 *
	 * This function displays every sections in a different form
	 */
	function show_forms() {
		?>
		<div class="metabox-holder">
			<?php foreach ( $this->settings_sections as $form ) { ?>
				<div id="<?php echo $form['id']; ?>" class="group" style="display: none;">
					<form method="post" action="options.php">
						<?php
						do_action( 'wsa_form_top_' . $form['id'], $form );
						settings_fields( $form['id'] );
						do_settings_sections( $form['id'] );
						do_action( 'wsa_form_bottom_' . $form['id'], $form );
						if ( isset( $this->settings_fields[ $form['id'] ] ) ):
						?>
						<div style="border-top: 1px #cccccc solid; margin-top: 20px;">
							<?php submit_button(); ?>
						</div>
						<?php endif; ?>
					</form>
				</div>
			<?php } ?>
		</div>
		<?php
		$this->script();
	}

	/**
	 * Tabbable JavaScript codes & Initiate Color Picker
	 *
	 * This code uses localstorage for displaying active tabs
	 */
	function script() {
		?>
		<script>
			jQuery(document).ready(function($) {
				//Initiate Color Picker
				$('.wp-color-picker-field').wpColorPicker();

				// Switches option sections
				$('.group').hide();
				var activetab = '';
				if (typeof(localStorage) != 'undefined' ) {
					activetab = localStorage.getItem("activetab");
				}

				//if url has section id as hash then set it as active or override the current local storage value
				if(window.location.hash){
					activetab = window.location.hash;
					if (typeof(localStorage) != 'undefined' ) {
						localStorage.setItem("activetab", activetab);
					}
				}

				if (activetab != '' && $(activetab).length ) {
					$(activetab).fadeIn();
				} else {
					$('.group:first').fadeIn();
				}
				$('.group .collapsed').each(function(){
					$(this).find('input:checked').parent().parent().parent().nextAll().each(
					function(){
						if ($(this).hasClass('last')) {
							$(this).removeClass('hidden');
							return false;
						}
						$(this).filter('.hidden').removeClass('hidden');
					});
				});

				if (activetab != '' && $(activetab + '-tab').length ) {
					$(activetab + '-tab').addClass('nav-tab-active');
				}
				else {
					$('.nav-tab-wrapper a:first').addClass('nav-tab-active');
				}
				$('.nav-tab-wrapper a').click(function(evt) {
					$('.nav-tab-wrapper a').removeClass('nav-tab-active');
					$(this).addClass('nav-tab-active').blur();
					var clicked_group = $(this).attr('href');
					if (typeof(localStorage) != 'undefined' ) {
						localStorage.setItem("activetab", $(this).attr('href'));
					}
					$('.group').hide();
					$(clicked_group).fadeIn();
					evt.preventDefault();
				});

				$('.wpsa-browse').on('click', function (event) {
					event.preventDefault();

					var self = $(this);

					// Create the media frame.
					var file_frame = wp.media.frames.file_frame = wp.media({
						title: self.data('uploader_title'),
						button: {
							text: self.data('uploader_button_text'),
						},
						multiple: false
					});

					file_frame.on('select', function () {
						attachment = file_frame.state().get('selection').first().toJSON();
						self.prev('.wpsa-url').val(attachment.url).change();
					});

					// Finally, open the modal
					file_frame.open();
				});
		});
		</script>
		<?php
		$this->_style_fix();
	}

	function _style_fix() {
		global $wp_version;

		if ( version_compare($wp_version, '3.8', '<=') ):
		?>
		<style type="text/css">
			/** WordPress 3.8 Fix **/
			.form-table th { padding: 20px 10px; }
			#wpbody-content .metabox-holder { padding-top: 5px; }
		</style>
		<?php
		endif;
	}
}

wp_utilities/class-widget-toc.php000066600000004116151747671010013166 0ustar00<?php
/**
 * Githuber_Widget_Toc
 * Add a Table of Content for your article. This widget is for single-post pages only.
 *
 * @package   WordPress
 * @author    Terry Lin <terrylinooo>
 * @license   GPLv3 (or later)
 * @link      https://terryl.in
 * @copyright 2018 Terry Lin
 */

/**
 * Githuber_Widget_Toc
 */
class Githuber_Widget_Toc extends WP_Widget {

	/**
	 * Sets up a new Githuber TOC widget instance.
	 */
	public function __construct() {

		$widget_ops = array(
			'classname'                   => 'widget_githuber_toc',
			'description'                 => __( 'Add a Table of Content for your article. This widget is for single-post pages only.', 'wp-githuber-md' ),
			'customize_selective_refresh' => true,
		);

		parent::__construct( 'githuber-toc', __( 'Githuber MD: TOC', 'wp-githuber-md' ), $widget_ops );
		$this->alt_option_name = 'widget_githuber_toc';
	}

	/**
	 * Initial TOC .
	 */
	public function githuber_toc_inline_js() {

	}

	/**
	 * Outputs the content for the Githuber TOC instance.
	 */
	public function widget( $args, $instance ) {
		$title = apply_filters( 'widget_title', $instance['title'] );
 
		$output = $args['before_widget'];

		if ( ! empty( $title ) ) {
			$output .= $args['before_title'];
			$output .= $title;
			$output .= $args['after_title'];
		}

		$output .= '<nav id="md-widget-toc" class="md-widget-toc" role="navigation"></nav>';
		$output .= $args['after_widget'];

		echo $output;
	}

	// Widget Backend 
	public function form( $instance ) {
		if ( isset( $instance[ 'title' ] ) ) {
			$title = $instance[ 'title' ];
		} else {
			$title = __( 'New title', 'wpb_widget_domain' );
		}
		// Widget admin form
	?>
		<p>
			<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> 
			<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
		</p>
	<?php 
	}

	/**
	 * Flushes the Githuber TOC widget cache.
	 */
	public function flush_widget_cache() {
		_deprecated_function( __METHOD__, '4.4.0' );
	}
}
helpers.php000066600000005711151747671010006740 0ustar00<?php
/**
 * Global helper functions.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.2.0
 */

/**
* Get the value of a settings field.
*
* @param string $option  settings field name.
* @param string $section the section name this field belongs to.
* @param string $default default text if it's not found.
* @return mixed
*/
function githuber_get_option( $option, $section, $default = '' ) {
	$options = get_option( $section );

	if ( isset( $options[ $option ] ) ) {
		return $options[ $option ];
	}
	return $default;
}

/**
 * Get current Post ID.
 *
 * @return int
 */
function githuber_get_current_post_id() {

	global $post;

	$post_id = null;

	if ( ! empty( $post ) )  {
		$post_id = $post->ID;
	} elseif ( ! empty( $_REQUEST['post'] ) ) {
		$post_id = $_REQUEST['post'];
	} elseif ( ! empty( $_REQUEST['post_ID'] ) ) {
		$post_id = $_REQUEST['post_ID'];
	}
	
	return $post_id;
}

/**
 * Check current user's permission.
 *
 * @param string $action User action.
 *
 * @return bool
 */
function githuber_current_user_can( $action ) {
	global $post;

	if ( current_user_can( $action, $post->ID ) ) {
		return true;
	}
	return false;
}

/**
 * Load view files.
 *
 * @param string $template_path The specific template's path.
 * @param array  $data              Data is being passed to.
 *
 * @return string
 */
function githuber_load_view( $template_path, $data = array() ) {
	$view_file_path = GITHUBER_PLUGIN_DIR . 'src/Views/' . $template_path . '.php';

	if ( ! empty( $data ) ) {
		extract( $data );
	}

	if ( file_exists( $view_file_path ) ) {
		ob_start();
		require $view_file_path;
		$result = ob_get_contents();
		ob_end_clean();
		return $result;
	}
	return null;
}

/**
 * Get post type on current screen.
 *
 * @return string
 */
function githuber_get_current_post_type() {
	global $post, $typenow, $current_screen;

	$post_type = null;

	if ( ! empty( $post ) && ! empty( $post->post_type ) ) {
		$post_type = $post->post_type;
	} elseif ( ! empty( $typenow ) ) {
		$post_type = $typenow;
	} elseif ( ! empty( $current_screen ) && ! empty( $current_screen->post_type ) ) {
		$post_type = $current_screen->post_type;
	} elseif ( ! empty( $_REQUEST['post_type'] ) ) {
		$post_type = sanitize_key( $_REQUEST['post_type'] );
	} elseif ( ! empty( $_REQUEST['post'] ) ) {
		$post_type = get_post_type( $_REQUEST['post'] );
	}
	return $post_type;
}

/**
 * Load utility files.
 *
 * @param string $filename
 *
 * @return string
 */
function githuber_load_utility( $filename ) {
	$include_path  = GITHUBER_PLUGIN_DIR . 'src/wp_utilities/class-' . $filename . '.php';

	if ( ! empty( $include_path ) && is_readable( $include_path ) ) {
		require $include_path;
	}
}

/**
 * Record Markdown processing logs for debug propose.
 *
 * @param string $message
 * @param array  $data
 *
 * @return void
 */
function githuber_logger( $message, $data = array() ) {
	if ( GITHUBER_DEBUG_MODE ) {
		\Githuber\Controller\Monolog::logger( $message, $data );
	}
}Views/setting/html-to-markdown.php000066600000002127151747671010013252 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.3.0
 * @version 1.3.0
 */
?>

<?php 

echo __( 'Dsiplaying A <strong>HTML to Markdown</strong> helper widget beside Markdown editor that helps you convert an <strong>old post</strong> into Markdown. <br />It is just a <strong>preview</strong>, to let you know what the converted content looks like after converting to Markdown.<br /><br />Notice: Turning on this option will force to disable <strong>auto-save</strong>, prevents breaking your original content.<br />If you are not satisfied with the result, do not click <strong>Update</strong> button.', 'wp-githuber-md');

?>

<script>
	(function($) {
		$(function() {
			var is_html_to_markdown = $('#wpuf-githuber_markdown-html_to_markdown-yes').is(':checked');

			if (is_html_to_markdown) {
				$('#wpuf-githuber_markdown-disable_autosave-yes').prop('checked', true);
				$('#wpuf-githuber_markdown-disable_autosave-no').prop('checked', false);
			}
		});
	})(jQuery);
</script>Views/setting/image-paste-smms.php000066600000000622151747671010013215 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.4.2
 * @version 1.4.2
 */
?>

<?php 

echo __( 'Required while the choosed storage space is <u>sm.ms</u>. If you don\'t have one, <a href="https://sm.ms/home/apitoken" target="_blank">sign up</a> here.', 'wp-githuber-md' );

Views/setting/image-paste.php000066600000000606151747671010012242 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.1
 */
?>

<?php 

echo __( 'Easily <a href="https://terryl.in/en/githuber-md-image-paste/" target="_blank">paste image from clipboard</a> directly into the post content.', 'wp-githuber-md' ); 
Views/setting/markdown-editor-switcher.php000066600000001044151747671010014777 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.7.0
 * @version 1.7.0
 */
?>

<?php echo __( 'This feature is for specific posts you don\'t want to use Markdown, allows you switch your Markdown editor to Rich editor (or Gutenberg)', 'wp-githuber-md'); ?><br />
<?php echo __( 'Be careful of using this feature - If you switch back to Rich editor then save, Markdown text will be cleared. ', 'wp-githuber-md'); ?><br />
Views/setting/image-paste-imgur.php000066600000001237151747671010013364 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.1
 */
?>

<?php 

echo __( 'Required while the choosed storage space is <u>imgur.com</u>. If you don\'t have one, <a href="https://api.imgur.com/oauth2/addclient" target="_blank">sign up</a> here.', 'wp-githuber-md' );

if ( ! function_exists( 'curl_init') ) {

	echo '<br /><span style="color: #b00000">';

	echo __( 'Uploading images to Imgur is unavailable because that <strong>PHP CURL</strong> is not installed on your system.', 'wp-githuber-md' );

	echo '</span>';
}
Views/setting/image-paste-media-library.php000066600000001271151747671010014760 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.6.1
 * @version 1.6.1
 */
?>

<?php echo __( 'This is Media Library that copy-paste images will be uploaded to, and generates different sizes of thumbnail.', 'wp-githuber-md' ); ?>
<br />
<?php echo __( 'If you would like to simply copy and paste images into your article and don’t care about the image management, , please choose No.', 'wp-githuber-md' ); ?>
<br />
<span style="color: #0081ab">
<?php echo __( 'Notice: This setting only works when Storage Space is set to `default`.', 'wp-githuber-md' ); ?>
</span>
Views/setting/about-github-repo.php000066600000001424151747671010013402 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>

<?php echo __( 'If you have any issues, or found any bugs, please report them in the following URL.', 'wp-githuber-md' ); ?><br />
<ul>
    <li><a href="https://github.com/terrylinooo/githuber-md" target="_blank">https://github.com/terrylinooo/githuber-md</a></li>
</ul>
<p style="border: 1px #80dfa8 solid; padding: 10px; background-color: #fff; marign: 10px;">
    <?php printf( __( 'WP Githuber MD recommends you install %s to increase your website security.', 'wp-githuber-md' ), '<a href="https://wordpress.org/plugins/wp-shieldon/" target="_blank">WP Shieldon</a>' ); ?>
</p>Views/setting/theme-adjustment.php000066600000001441151747671010013322 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.2.0
 */
?>

<pre style="font-size: 13px;">
remove_action( 'wp_head', 'feed_links_extra' );                
remove_action( 'wp_head', 'feed_links' );
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'wlwmanifest_link' );
remove_action( 'wp_head', 'index_rel_link' );
remove_action( 'wp_head', 'parent_post_rel_link' );
remove_action( 'wp_head', 'start_post_rel_link' );
remove_action( 'wp_head', 'adjacent_posts_rel_link' );
remove_action( 'wp_head', 'wp_generator' );
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
remove_action( 'wp_head', 'wp_shortlink_wp_head' );
</pre>Views/setting/markdown-extra.php000066600000001215151747671010013006 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.1
 */
?>

<?php 

echo __( 'Support <a href="https://michelf.ca/projects/php-markdown/extra/" target="_blank">Markdown Extra</a>.', 'wp-githuber-md' );

if ( ! class_exists( 'DOMDocument' ) ) {

	echo '<br /><span style="color: #b00000">';

	echo __( 'Markdown Extra parser requires PHP module <strong>libxml</strong> and your system does not have <strong>libxml</strong> installed. Please disable Markdown Extra.', 'wp-githuber-md' );

	echo '</span>';
}
Views/setting/theme-description.php000066600000001245151747671010013471 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.5.0
 */
?>

<a href="https://github.com/terrylinooo/mynote">
    <img src="<?php echo GITHUBER_PLUGIN_URL . 'assets/images/mynote-theme-demo.png' ?>">
</a><br /><br />

<p style="border: 1px #80dfa8 solid; padding: 10px; background-color: #fff; marign: 10px;">
<?php echo __( 'Mynote is a WordPress theme built for developer. Click <a href="https://wordpress.org/themes/mynote/" target="_blank">here</a> to download it from WordPress theme dictionary.', 'wp-githuber-md' ); ?>
</p>
Views/setting/smart-quotes.php000066600000000734151747671010012514 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.7.0
 * @version 1.7.0
 */
?>

<?php echo __( 'This is WordPress default, you should not turn it off unless you know what you do.', 'wp-githuber-md' ); ?> 
<a href="https://developer.wordpress.org/reference/functions/wptexturize/" target="_blank"><?php echo __( '(detail)', 'wp-githuber-md' ); ?></a>Views/metabox/fetch-remote-image.php000066600000001210151747671010013462 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/FetchRemoteImage
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.3.0
 * @version 1.3.0
 */
?>

<div class="submitbox p-r">
	<div class="misc-publishing-actions">
		<table>
			<tr>
				<td style="vertical-align: top">
					<input type="hidden" name="fetch_remote_image" value="no">
					<input type="checkbox" name="fetch_remote_image" value="yes">
				</td>
				<td>
                    <?php echo __( 'Fetch remote images and save them into local folder.', 'wp-githuber-md'  ); ?>
				</td>
			</tr>
		</table>
	</div>
</div>
Views/metabox/keyword-suggestion-tool.php000066600000002304151747671010014651 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/KeywordSuggestion
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.5.0
 * @version 1.5.0
 */
?>

<div class="submitbox p-r">
	<div class="misc-publishing-actions">
		<p>
			<?php echo __( 'Enter a keyword that you want to fetch its related long-tail terms.', 'wp-githuber-md'  ); ?>
		</p>
		<table>
			<tr>
				<td>
					<input type="text" name="ks_keyword">
					<input type="hidden" name="ks_nonce" value="<?php echo wp_create_nonce( 'keyword_suggession_action' ); ?>">
				</td>
            </tr>
            <tr>
                <td id="display-keyword-suggestion"></td>
            </tr>
		</table>
	</div>
	<div class="clear"></div>
	<hr />
	<div class="major-publishing-actions" style="text-align: right; padding-top: 3px;">
		<div class="publishing-action">
			<button id="btn-keyword-suggestion-reset" type="button" class="button button-large"><?php echo __( 'Clear', 'wp-githuber-md'  ); ?></button>&nbsp;
			<button id="btn-keyword-suggestion-query" class="button button-primary button-large" type="button"><?php echo __( 'Query', 'wp-githuber-md'  ); ?></button>
		</div>
	</div>
</div>
Views/metabox/custom-media-library.php000066600000000733151747671010014062 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/CustomMediaLibrary
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.6.2
 * @version 1.6.2
 */

?>
<select class="githuber_image_insert" name="githuber_image_insert">
    <option value="markdown" selected><?php echo __( 'Markdown', 'wp-githuber-md'  ); ?></option>
    <option value="html"><?php echo __( 'HTML', 'wp-githuber-md'  ); ?></option>
</select>
Views/metabox/markdown-per-post.php000066600000003371151747671010013423 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/HtmlToMarkdown
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.6.0
 * @version 1.6.0
 */
if ( ! isset( $markdown_this_post_choice ) ) {
   return;
}
?>
<div class="submitbox p-r">
	<div class="misc-publishing-actions">
		<?php if ( 'no' !== $markdown_this_post_choice ) : ?>
		<div class="wpmd">
			<?php if ( $is_markdown_this_post ) : ?>
				<input type="checkbox" name="markdown_this_post" id="markdown-switch" value="yes" checked /><label for="markdown-switch">Toggle</label>
			<?php else : ?>
				<input type="checkbox" name="markdown_this_post" id="markdown-switch" value="yes" /><label for="markdown-switch">Toggle</label>
			<?php endif; ?>
		</div>
		<?php else : ?>
		<div class="wpmd">
			<input type="checkbox" name="markdown_this_post" id="markdown-switch" value="yes" /><label for="markdown-switch">Toggle</label>
		</div>
		<?php endif; ?>
	</div>
</div>

<?php if ( 'yes' == githuber_get_option( 'support_mathjax', 'githuber_modules' ) ) : ?>
<!-- BEGIN - This section is a templete for MathJax module -->
<script type="text/x-mathjax-config"> 
	MathJax.Hub.Config({
		showProcessingMessages: false,
		messageStyle: "none",
		extensions: [
			"tex2jax.js",
			"TeX/mediawiki-texvc.js",
			"TeX/noUndefined.js",
			"TeX/autoload-all.js",
			"TeX/AMSmath.js",
			"TeX/AMSsymbols.js"
		],
		jax: [
			"input/TeX",
			"output/SVG"
		],
		elements: document.getElementsByClassName("mathjax"),
		tex2jax: {
			skipTags: [
				"script",
				"noscript",
				"style",
				"textarea"
			],
			processClass: "mathjax"
		},
		processEscapes: true,
		preview: "none"
	});
</script>
<!-- END - This section is a templete for MathJax module -->
<?php endif; ?>

Views/metabox/html-to-markdown.php000066600000003320151747671010013230 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/HtmlToMarkdown
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.3.0
 * @version 1.3.0
 */
?>

<div class="submitbox p-r">
	<div class="misc-publishing-actions">
		<p>
			<?php echo __( 'This is a tool that helps you easily convert an old post into Markdown. If you are not satisfied with the result, do not click <strong>Update</strong> button.', 'wp-githuber-md'  ); ?>
		</p>
		<table>
			<tr>
				<td>
					<?php echo __( 'Strip tags', 'wp-githuber-md'  ); ?>:
				</td>
				<td>
					&nbsp;&nbsp;
					<input type="radio" name="h2m_strip_tags" value="yes" checked> <?php echo __( 'Yes', 'wp-githuber-md'  ); ?>
					&nbsp;&nbsp;
					<input type="radio" name="h2m_strip_tags" value="no"> <?php echo __( 'No', 'wp-githuber-md'  ); ?> 
				</td>
			</tr>
			<tr>
				<td>
					<?php echo __( 'Line break', 'wp-githuber-md'  ); ?>:
				</td>
				<td>
					&nbsp;&nbsp;
					<input type="radio" name="h2m_line_break" value="yes" checked> <?php echo __( 'Yes', 'wp-githuber-md'  ); ?>
					&nbsp;&nbsp;
					<input type="radio" name="h2m_line_break" value="no"> <?php echo __( 'No', 'wp-githuber-md'  ); ?><br />
				</td>
			</tr>
		</table>
	</div>
	<div class="clear"></div>
	<hr />
	<div class="major-publishing-actions" style="text-align: right; padding-top: 3px;">
		<div class="publishing-action">
			<button type="button" class="button button-large" onclick="location.reload();"><?php echo __( 'Reload', 'wp-githuber-md'  ); ?></button>&nbsp;
			<button id="btn-html2markdown" class="button button-primary button-large" type="button"><?php echo __( 'Convert', 'wp-githuber-md'  ); ?></button>
		</div>
	</div>
</div>
Views/example/mermaid.php000066600000001660151747671010011443 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>

<pre class="prettyprint setting-example">
<code class="language-markdown">
```mermaid
sequenceDiagram
participant Alice
participant Bob
Alice->>John: Hello John, how are you?
loop Healthcheck
John->>John: Fight against hypochondria
end
Note right of John: Rational thoughts<br/>prevail...
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!
```
</code>
</pre>
<p class="description"><?php echo __( 'Block identification code:', 'wp-githuber-md' ); ?> <span class="example-tag">mermaid</span></p>
<p class="description"><?php echo __( 'The Markdown text above will be rendered to:', 'wp-githuber-md' ); ?></p>

<pre class="setting-example"><img src="<?= GITHUBER_PLUGIN_URL ?>assets/images/demo_mermaid.gif"></pre>

Views/example/prism.php000066600000003000151747671010011145 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>
<p style="color: #aa0000"><?php echo __( 'If you switch to another highlighter moudle, you have to update every your post to take effect.<br />Because only the language files defined in the code block will be loaded, not all fat packed file.', 'wp-githuber-md' ); ?> </p>
<pre class="prettyprint setting-example">
<code class="language-markdown">
```php
function sayHello() {
	return &#39;Hello! World.&#39;;
}

echo sayHello();
```
</code>
</pre>
<p class="description"><?php echo __( 'Block identification code:', 'wp-githuber-md' ); ?> 
<a href="https://terryl.in/en/prism-js-html-code-language-list-for-syntax-highlighting/" target="_blank">
<?php echo __( 'Check out this page to view full list.', 'wp-githuber-md' ); ?></a>
</p>

<script>

	(function($) {
		$(function() {

			$('#wpuf-githuber_modules-support_prism').click(function() {
				if ($(this).is(':checked')) {
					if ($('#wpuf-githuber_modules-support_highlight').is(':checked')) {
						$('#wpuf-githuber_modules-support_highlight').trigger('click');
					}
				}
			});

			$('#wpuf-githuber_modules-support_highlight').click(function() {
				if ($(this).is(':checked')) {
					if ($('#wpuf-githuber_modules-support_prism').is(':checked')) {
						$('#wpuf-githuber_modules-support_prism').trigger('click');
					}
				}
			});
		});
	})(jQuery);

</script>

Views/example/mathjax.php000066600000002045151747671010011457 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.7.0
 * @version 1.7.0
 */
?>

<p class="description"><?php echo __( 'MathJax block:', 'wp-githuber-md' ); ?></p>

<pre class="prettyprint setting-example">
<code class="language-markdown">
```mathjax
f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi
```
</code>
</pre>
<p class="description"><?php echo __( 'Block identification code:', 'wp-githuber-md' ); ?> <span class="example-tag">mathjax</span></p>
<p class="description"><?php echo __( 'MathJax inline:', 'wp-githuber-md' ); ?></p>

<pre class="prettyprint setting-example">
<code class="language-markdown">
`$ f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi $`
</code>
</pre>

<p class="description"><?php echo __( 'The Markdown text above will be rendered to:', 'wp-githuber-md' ); ?></p>

<pre class="setting-example"><img src="<?= GITHUBER_PLUGIN_URL ?>assets/images/demo_katex.gif"></pre>

Views/example/flowchart.php000066600000001522151747671010012013 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>

<pre class="prettyprint setting-example">
<code class="language-markdown">
```flow
st=>start: User login
op=>operation: Operation
cond=>condition: Successful Yes or No?
e=>end: Into admin

st->op->cond
cond(yes)->e
cond(no)->op
```
</code>
</pre>
<p class="description"><?php echo __( 'Block identification code:', 'wp-githuber-md' ); ?> <span class="example-tag">flow</span><span class="example-tag">flowchart</span></p>
<p class="description"><?php echo __( 'The Markdown text above will be rendered to:', 'wp-githuber-md' ); ?></p>

<pre class="setting-example"><img src="<?= GITHUBER_PLUGIN_URL ?>assets/images/demo_flowchart.gif"></pre>

Views/example/sequence.php000066600000001350151747671010011631 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>

<pre class="prettyprint setting-example">
<code class="language-markdown">
```seq
A->B: Message
B->C: Message
C->A: Message
```
</code>
</pre>
<p class="description"><?php echo __( 'Block identification code:', 'wp-githuber-md' ); ?> <span class="example-tag">seq</span> <span class="example-tag">sequence</span></p>
<p class="description"><?php echo __( 'The Markdown text above will be rendered to:', 'wp-githuber-md' ); ?></p>

<pre class="setting-example"><img src="<?= GITHUBER_PLUGIN_URL ?>assets/images/demo_sequence.gif"></pre>

Views/example/inline-code-keyboard-style.php000066600000001264151747671010015147 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.8.4
 */
?>

<pre class="prettyprint setting-example">
<code class="language-markdown">
Happy Markdowning!!

1. Use `{ctrl}`+`{c}` to copy text.
2. Use `{ctrl}`+`{v}` to paste text.
3. Open task manager: `{ctrl}`+`{alt}`+`{del}`
</code>
</pre>

<p class="description"><?php echo __( 'The Markdown text above will be rendered to:', 'wp-githuber-md' ); ?></p>

<pre class="setting-example"><img src="<?= GITHUBER_PLUGIN_URL ?>assets/images/demo_inline_keyboard.gif"></pre>
Views/example/katex.php000066600000002037151747671010011140 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>

<p class="description"><?php echo __( 'KaTeX block:', 'wp-githuber-md' ); ?></p>

<pre class="prettyprint setting-example">
<code class="language-markdown">
```katex
f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi
```
</code>
</pre>
<p class="description"><?php echo __( 'Block identification code:', 'wp-githuber-md' ); ?> <span class="example-tag">katax</span></p>
<p class="description"><?php echo __( 'KaTeX inline:', 'wp-githuber-md' ); ?></p>

<pre class="prettyprint setting-example">
<code class="language-markdown">
`$$ f(x) = \int_{-\infty}^\infty\hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi $$`
</code>
</pre>

<p class="description"><?php echo __( 'The Markdown text above will be rendered to:', 'wp-githuber-md' ); ?></p>

<pre class="setting-example"><img src="<?= GITHUBER_PLUGIN_URL ?>assets/images/demo_katex.gif"></pre>

Views/example/gfm-task-list.php000066600000001113151747671010012500 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>

<pre class="setting-example">
<code class="language-markdown">
- [x] Finish my changes
- [ ] Push my commits to GitHub
- [ ] Open a pull request
</code>
</pre>
<p class="description"><?php echo __( 'The Markdown text above will be rendered to:', 'wp-githuber-md' ); ?></p>

<pre class="setting-example"><img src="<?= GITHUBER_PLUGIN_URL ?>assets/images/demo_tasklist.gif"></pre>

Views/example/html5-figure.php000066600000001312151747671010012327 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>

<pre class="prettyprint setting-example">
<code class="language-markdown">
%[Alt text](http://yoururl.com/test.jpg "Figcaption text")
</code>
</pre>
<?php

echo __( 'The Markdown text above will be transformed to:', 'wp-githuber-md' );

?><br />

<pre class="prettyprint setting-example">
<code class="language-html">
&lt;figure&gt;
    &lt;img src=&quot;http://yoururl.com/test.jpg&quot; alt=&quot;Alt text&quot;&gt;
    &lt;figcaption&gt;Figcaption text&lt;/figcaption&gt;
&lt;/figure&gt;
</code>
</pre>

Views/example/highlight-js.php000066600000001664151747671010012412 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * View for Controller/Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.2.0
 * @version 1.3.0
 */
?>
<p style="color: #aa0000"><?php echo __( 'If you switch to another highlighter moudle, you have to update every your post to take effect.<br />Because only the language files defined in the code block will be loaded, not all fat packed file.', 'wp-githuber-md' ); ?> </p>
<pre class="prettyprint setting-example">
<code class="language-markdown">
```php
function sayHello() {
	return &#39;Hello! World.&#39;;
}

echo sayHello();
```
</code>
</pre>
<p class="description"><?php echo __( 'Block identification code:', 'wp-githuber-md' ); ?> 
<a href="https://terryl.in/en/highlight-js-html-code-language-list-for-syntax-highlighting/" target="_blank">
<?php echo __( 'Check out this page to view full list.', 'wp-githuber-md' ); ?></a>
</p>



Views/message/php-libxml-warning.php000066600000001026151747671010013531 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * Show PHP module notice.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.7.0
 * @version 1.7.0
 */
$php_version = phpversion();
?>

<div class="notice notice-error is-dismissible" style="margin-top: 15px;">
	<p>
		<?php echo __( 'Markdown Extra parser requires PHP module <strong>libxml</strong> and your system does not have <strong>libxml</strong> installed. Please disable Markdown Extra.', 'wp-githuber-md' ); ?> <br>
	</p>
</div>Views/message/php-mbstring-warning.php000066600000001075151747671010014073 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * Show PHP module notice.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.7.0
 * @version 1.7.0
 */
$php_version = phpversion();
?>

<div class="notice notice-error is-dismissible" style="margin-top: 15px;">
	<p>
		<?php printf( __( 'Markdown parser requires PHP module <strong>mbstring</strong> and your system does not have <strong>mbstring</strong> installed. Please ask for your web hosting provider to help you.', 'wp-githuber-md' ), $php_version ) ?> <br>
	</p>
</div>Views/message/php-version-warning.php000066600000001110151747671010013721 0ustar00<?php 
if ( ! defined('GITHUBER_PLUGIN_NAME') ) die; 
/**
 * Show PHP version notice.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.4.3
 * @version 1.4.3
 */
$php_version = phpversion();
?>

<div class="notice notice-error is-dismissible">
	<p>
		<?php printf( __( 'The minimum required PHP version for WP Githuber MD is PHP <strong>5.3.6</strong>, and yours is <strong>%1s</strong>.', 'wp-githuber-md' ), $php_version ) ?> <br>
		<?php echo __( 'Please remove WP Githuber MD or upgrade your PHP version.', 'wp-githuber-md' ); ?>
	</p>
</div>Models/Markdown.php000066600000001236151747671010010301 0ustar00<?php
/**
 * Class Markdown
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.4.3
 */

namespace Githuber\Model;

class Markdown extends ModelAbstract {

	/**
	 * Constructer.
	 * 
	 * @return void
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Get the latest post revision.
	 *
	 * @param int $post_id The post ID
	 *
	 * @return object Post data
	 */
	function get_lastest_revision( $post_id ) {
		return $this->db->get_row(
			$this->db->prepare(
				"SELECT * FROM {$this->db->posts} WHERE post_type = 'revision' AND post_parent = %d ORDER BY ID DESC", 
				$post_id
			)
		);
	}
}Models/ModelAbstract.php000066600000001016151747671010011237 0ustar00<?php

/**
 * Class ModelAbstract
 * 
 * Models are specifically used for dealing with the data exchange between controller and database.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.0.0
 */

namespace Githuber\Model;

abstract class ModelAbstract {

	/**
	 * WP DB instance.
	 *
	 * @var object
	 */
	public $db;

	/**
	 * Constructer.
	 * 
	 * @return void
	 */
	public function __construct() {

		// Get WP DB object.
		global $wpdb;

		$this->db = &$wpdb;
	}
}
Controllers/RichEditing.php000066600000003102151747671010011765 0ustar00<?php
/**
 * Class RichEditing
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.6.0
 * @version 1.6.0
 */

namespace Githuber\Controller;
use Githuber\Controller\Monolog as Monolog;

class RichEditing {

	const MD_POST_META_ENABLED  = '_is_githuber_markdown_enabled';

	/**
	 * Constructer.
	 */
	public function __construct() {

	}

	/**
	 * Enable rich editor.
	 */
	public function enable() {
		add_action( 'admin_init', array( $this, '_rich_editing_true' ) );
	}
		
	/**
	 * Enable rich editor.
	 */
	public function disable() {
		add_action( 'admin_init', array( $this, '_rich_editing_false' ) );
	}
		
	/**
	 * Apply hook for enabling rich editor.
	 */
	public function _rich_editing_true() {
		global $current_user;

		if ( ! user_can_richedit() ) {
			update_user_option( $current_user->ID, 'rich_editing', 'true', true );
		}
		add_filter( 'user_can_richedit' , '__return_true', 50 );
	}

		/**
	 * Apply hook for disabling rich editor.
	 */
	public function _rich_editing_false() {
		global $current_user;

		if ( user_can_richedit() ) {
			update_user_option( $current_user->ID, 'rich_editing', 'false', true );
		}
		add_filter( 'user_can_richedit' , '__return_false', 50 );
	}

	/**
	 * Enable Gutenberg.
	 */
	public function enable_gutenberg() {
		if ( $GLOBALS['wp_version'] > '5.0' ) {
			add_filter('use_block_editor_for_post', '__return_true', 5);
		}
	}

	/**
	 * Disable Gutenberg.
	 */
	public function disable_gutenberg() {
		if ( $GLOBALS['wp_version'] > '5.0' ) {
			add_filter('use_block_editor_for_post', '__return_false', 5);
		}
	}
}
Controllers/HtmlToMarkdown.php000066600000007617151747671010012525 0ustar00<?php
/**
 * Class HtmlToMarkdown
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.3.0
 * @version 1.4.2
 */

namespace Githuber\Controller;
use League\HTMLToMarkdown\HtmlConverter;

class HtmlToMarkdown extends ControllerAbstract {

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'admin_init' ) );
	}

	/**
	 * Initalize to WP `admin_init` hook.
	 */
	public function admin_init() {
		$user          = wp_get_current_user();
		$allowed_roles = array( 'editor', 'administrator', 'author' );

		// For security reasons, only authorized logged-in users can update content.
		if ( array_intersect( $allowed_roles, $user->roles ) || is_super_admin() ) {
			add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
			add_action( 'wp_ajax_githuber_html2markdown', array( $this, 'admin_githuber_html2markdown' ) );

			// Add the sidebar metabox to posts.
			add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );

			// Remove auto-save function.
			add_action( 'admin_enqueue_scripts', array( $this , 'remove_autosave' ), 100 );
		}
	}

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {

	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {
		wp_enqueue_script( 'githuber-md-h2m', $this->githuber_plugin_url . 'assets/js/githuber-md-h2m.js', array(), $this->version, true );

		$data['ajax_url'] = admin_url( 'admin-ajax.php' );
		$data['post_id']  = githuber_get_current_post_id();

		wp_localize_script( 'githuber-md-h2m', 'h2m_config', $data );
	}

	/**
	 * Remove auto-save function.
	 */
	function remove_autosave() {
		wp_dequeue_script('autosave');
	}

	/**
	 * Register the `HtmlToMarkdown` meta box in the post-editor.
	 */
	public function add_meta_box() {
		
		if ( ! githuber_current_user_can( 'edit_posts' ) ) {
			return false;
		}

		add_meta_box(
			'html2markdown_meta_box',
			__( 'HTML to Markdown', 'wp-githuber-md' ) . '<div class="bg-icon-md"></div>',
			array( $this, 'show_meta_box' ),
			null,
			'side',
			'high'
		);
	}
	
	/**
	 * Show `HtmlToMarkdown` meta box.
	 */
	public function show_meta_box() {
		echo githuber_load_view( 'metabox/html-to-markdown' );
	}

	/**
	 * Do action hook for image paste.
	 */
	public function admin_githuber_html2markdown() {
		$is_strip_tags = false;
		$is_line_break = false;
		$post_content  = '';

		$response = array(
			'success' => false,
			'result'  => '',
		);

		if ( isset( $_POST['strip_tags'] ) && 'yes' === $_POST['strip_tags'] ) {
			$is_strip_tags = true;
		}

		if ( isset( $_POST['line_break'] ) && 'yes' === $_POST['line_break'] ) {
			$is_line_break = true;
		}

		if ( ! isset( $_POST['post_id'] ) ) {
			//return;
		}

		if ( ! empty( $_POST['post_content'] ) ) {
			$post_content = $_POST['post_content'];
		}

		//$post_id = (int) $_POST['post_id'];
		//$post    = (array) get_post( $post_id );

		$converter = new HtmlConverter();
		$converter->getConfig()->setOption('strip_tags', $is_strip_tags);
		$converter->getConfig()->setOption('hard_break', $is_line_break);
		$converter->getConfig()->setOption('header_style', 'atx');

		$markdown = $converter->convert( $post_content );
		$markdown = $this->filter_wordpress_html( $markdown );

		if ( ! empty( $markdown ) ) {
			$response = array(
				'success' => true,
				'result'  => $markdown,
			);
		}

		header('Content-type: application/json');
		
		echo json_encode( $response );

		// To avoid wp_ajax return "0" string to break the vaild json string.
		wp_die();
	}

	/**
	 * Strip slash and quotes that added by jQuery AJAX.
	 *
	 * @param string HTML string
	 * @return string
	 */
	private function filter_wordpress_html( $content ) {
		$content = str_replace( '\\"', '', $content );
		$content = wp_unslash( $content );
		return $content;
	}
}
Controllers/CustomMediaLibrary.php000066600000002517151747671010013344 0ustar00<?php
/**
 * Class Custom Media Livrary
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.6.2
 * @version 1.6.2
 */

namespace Githuber\Controller;

class CustomMediaLibrary extends ControllerAbstract {

	/**
	 * Constructer.
	 */
	public function __construct() {
        parent::__construct();
	}

	/**
	 * Initialize.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'admin_init' ) );
    }

	/**
	 * Initalize to WP `admin_init` hook.
	 */
	public function admin_init() {
        add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields_to_edit' ) , 10, 2 );
    }

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {

	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {

	}

    /**
     * Show custom media field.
     *
     * @param array $form_fields
     * @param object $post
     * @return void
     */
    function attachment_fields_to_edit( $form_fields, $post = null ) {
 
        $form_fields['githuber_image_insert'] = array(
            'value' => 'markdown',
            'label' => __( 'Code type', 'wp-githuber-md' ),
            'input' => 'html',
            'html'  => githuber_load_view( 'metabox/custom-media-library' ),
        );

        return $form_fields;
    }
}
Controllers/Markdown.php000066600000116403151747671010011367 0ustar00<?php
/**
 * Class Markdown
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.11.0
 *
 * A lot of code snippets are from Jetpack Markdown module, we don't reinvent the wheel, however, we modify it for our needs.
 * @link https://github.com/Automattic/jetpack/blob/master/modules/markdown/easy-markdown.php
 */

namespace Githuber\Controller;
use Githuber\Controller as Controller;
use Githuber\Module as Module;
use Githuber\Model as Model;

class Markdown extends ControllerAbstract {

	/**
	 * We use a JavaScript library that is called `EditorMd`, and this is its version number.
	 *
	 * @link https://github.com/pandao/editor.md
	 *
	 * @var string
	 */
	public $editormd_varsion = '1.5.0.14';

	/**
	 * The Post Type support from Markdown controller.
	 *
	 * @var string
	 */
	public $post_type;

	/**
	 * Constants.
	 */
	const MD_POST_TYPE           = 'githuber_markdown';
	const MD_POST_META           = '_is_githuber_markdown';
	const MD_POST_META_ENABLED   = '_is_githuber_markdown_enabled';
	const MD_POST_META_PRISM     = '_githuber_prismjs';
	const MD_POST_META_HIGHLIGHT = '_githuber_highlightjs';
	const MD_POST_META_SEQUENCE  = '_is_githuber_sequence';
	const MD_POST_META_FLOW      = '_is_githuber_flow_chart';
	const MD_POST_META_KATEX     = '_is_githuber_katex';
	const MD_POST_META_MATHJAX   = '_is_githuber_mathjax';
	const MD_POST_META_MERMAID   = '_is_githuber_mermaid';

	const JETPACK_MD_POST_META   = '_wpcom_is_markdown';

	/**
	 * Parser's instance.
	 */
	private static $parser_instance;

	/**
	 * Markdown Model instance.
	 */
	private static $model_instance;

	/**
	 * Flags
	 *
	 * @var array
	 */
	private $monitoring = array( 'post' => array(), 'parent' => array() );

	/**
	 * To ensure that our munged posts over xml-rpc are removed from the cache.
	 *
	 * @var array
	 */
	public $posts_to_uncache = array();

	/**
	 * Module supprt.
	 *
	 * @var boolean
	 */
	public $is_support_prism     = false;
	public $is_support_highlight = false;
	public $is_support_task_list = false;
	public $is_support_katex     = false;
	public $is_support_flowchart = false;
	public $is_support_sequence  = false;
	public $is_support_mermaid   = false;
	public $is_support_toc       = false;
	public $is_support_mathjax   = false;

	public $markdown_this_post = true;

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();

		if ( ! self::$model_instance ) {
			self::$model_instance = new Model\Markdown();
		}

		if ( 'yes' === githuber_get_option( 'support_prism', 'githuber_modules' ) ) {
			$this->is_support_prism = true;
		}

		if ( 'yes' === githuber_get_option( 'support_highlight', 'githuber_modules' ) ) {
			$this->is_support_highlight = true;
		}

		if ( 'yes' === githuber_get_option( 'support_task_list', 'githuber_extensions' ) ) {
			$this->is_support_task_list = true;
		}

		if ( 'yes' === githuber_get_option( 'support_katex', 'githuber_modules' ) ) {
			$this->is_support_katex = true;
		}

		if ( 'yes' === githuber_get_option( 'support_flowchart', 'githuber_modules' ) ) {
			$this->is_support_flowchart = true;
		}

		if ( 'yes' === githuber_get_option( 'support_sequence_diagram', 'githuber_modules' ) ) {
			$this->is_support_sequence = true;
		}

		if ( 'yes' === githuber_get_option( 'support_mermaid', 'githuber_modules' ) ) {
			$this->is_support_mermaid = true;
		}

		if ( 'yes' === githuber_get_option( 'support_mathjax', 'githuber_modules' ) ) {
			$this->is_support_mathjax = true;
		}

		// Load TOC widget. //
		if ( 'yes' == githuber_get_option( 'support_toc', 'githuber_modules' ) ) {
			if ( 'yes' == githuber_get_option( 'display_toc_in_post', 'githuber_modules' ) ) {
				$this->is_support_toc = true;
			}
		}
	}

	/**
	 * Initialize.
	 */
	public function init() {

		// Force-disable Jetpack's Markdown module if it is active.
		add_filter( 'option_jetpack_active_modules', array( $this, 'admin_githuber_disable_jetpack_markdown' ) );

		$enabled_post_types = githuber_get_option( 'enable_markdown_for_post_types', 'githuber_markdown' );

		if ( empty( $enabled_post_types ) ) {
			$enabled_post_types = array(
				'post',
				'page',
			);
		}

		foreach( $enabled_post_types as $post_type ) {
			$support_post_types[] = $post_type;
		}

		$support_post_types = apply_filters( 'githuber_md_suppot_post_types', $support_post_types );

		array_push( $support_post_types , 'revision');

		foreach ( $support_post_types as $post_type ) {
			add_post_type_support( $post_type, self::MD_POST_TYPE );

			// Only use it in DEBUG mode.
			githuber_logger( 'add_post_type_support', array( $post_type ) );
		}

		add_action( 'admin_init', array( $this, 'admin_init' ) );

		$post_id = githuber_get_current_post_id();

		$markdown_this_post = get_metadata( 'post', $post_id, self::MD_POST_META_ENABLED, true );

		// Get post type from curren screen.
		$current_post_type = githuber_get_current_post_type();

		// Feature request #98
		if ( 'yes' === githuber_get_option( 'richeditor_by_default', 'githuber_preferences' ) ) {

			if ( empty( $markdown_this_post ) || 'yes' !== $markdown_this_post ) {
				$rich_editing = new RichEditing();
				$rich_editing->enable();

				if ( empty( $current_post_type ) || 'post' === $current_post_type || 'page' === $current_post_type ) {
					$rich_editing->enable_gutenberg();
				}

				$this->markdown_this_post = false;
			}
		}

		if ( ! empty( $current_post_type ) && ! post_type_supports( githuber_get_current_post_type(), self::MD_POST_TYPE ) ) {

			// We enable Rich editor if user not enable Markdown for current post type!
			$rich_editing = new RichEditing();
			$rich_editing->enable();

			// Custom post types are not supporting Gutenberg by default for now, so
			// We only enable Gutenberg for `post` and `page`...
			if ( 'post' === $current_post_type || 'page' === $current_post_type ) {
				$rich_editing->enable_gutenberg();
			}
		} else {

			// Markdown-per-post switcher.
			if ( 'no' === $markdown_this_post ) {
				$rich_editing = new RichEditing();
				$rich_editing->enable();

				if ( 'post' === $current_post_type || 'page' === $current_post_type ) {
					$rich_editing->enable_gutenberg();
				}

			} else {

				// Tell YoastSEO, the Markdown is enable.
				if ( 'yes' === githuber_get_option( 'support_wpseo_analysis', 'githuber_preferences' ) ) {
					add_filter( 'wpseo_is_markdown_enabled', '__return_true' );
				}

				// Okay! User enable Markdown for current current post and it's post type.
				$this->jetpack_code_snippets();

				if ( 'yes' === githuber_get_option( 'html_to_markdown', 'githuber_markdown' ) ) {
					$html2markdown = new Controller\HtmlToMarkdown();
					$html2markdown->init();
				}

				if ( 'yes' === githuber_get_option( 'fetch_remote_image', 'githuber_markdown' ) ) {
					$fetchRemoteImage = new Controller\FetchRemoteImage();
					$fetchRemoteImage->init();
				}
			}
		}
	}

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {
		wp_enqueue_style( 'editmd', $this->githuber_plugin_url . '/assets/vendor/editor.md/css/editormd.min.css', array(), $this->editormd_varsion, 'all' );
	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {

		if ( ! post_type_supports( get_current_screen()->post_type, self::MD_POST_TYPE ) ) {
			return;
		}

		$post_id = githuber_get_current_post_id();
		$markdown_this_post = get_metadata( 'post', $post_id, self::MD_POST_META_ENABLED, true );

		if ( 'no' === $markdown_this_post || ! $this->markdown_this_post ) {

		} else {
			wp_enqueue_script( 'editormd', $this->githuber_plugin_url . 'assets/vendor/editor.md/editormd.min.js', array( 'jquery' ), $this->editormd_varsion, true );
			wp_enqueue_script( 'githuber-md', $this->githuber_plugin_url . 'assets/js/githuber-md.js', array( 'editormd' ), $this->version, true );

			switch ( get_bloginfo( 'language' ) ) {
				case 'zh-TW':
					wp_enqueue_script( 'editor-md-lang', $this->githuber_plugin_url . 'assets/vendor/editor.md/languages/zh-tw.js', array(), $this->editormd_varsion, true );
					break;

				case 'zh-CN':
					wp_enqueue_script( 'editor-md-lang', $this->githuber_plugin_url . 'assets/vendor/editor.md/languages/zh-cn.js', array(), $this->editormd_varsion, true );
					break;

				case 'en-US':
				default:
					wp_enqueue_script( 'editor-md-lang', $this->githuber_plugin_url . 'assets/vendor/editor.md/languages/en.js', array(), $this->editormd_varsion, true );
			}

			$editormd_config_list['markdown'] = array(
				'editor_sync_scrolling',
				'editor_live_preview',
				'editor_image_paste',
				'editor_html_decode',
				'editor_toolbar_theme',
				'editor_editor_theme',
				'editor_line_number',
				'editor_spell_check',
				'editor_spell_check_lang',
				'editor_match_highlighter',
			);

			$editormd_config_list['modules'] = array(
				'support_emojify',
				'support_katex',
				'support_flowchart',
				'support_sequence_diagram',
				'support_mermaid',
				'support_mathjax',
			);

			$editormd_config_list['extensions'] = array(
				'support_task_list',
				'support_inline_code_keyboard_style',
				'support_html_figure',
			);

			$editormd_localize = array();

			foreach ( $editormd_config_list as $key => $value ) {
				foreach ( $value as $setting_name ) {
					$editormd_localize[ $setting_name ] = githuber_get_option( $setting_name, 'githuber_' . $key );
				}
			}

			$editormd_localize['editor_modules_url']   = $this->githuber_plugin_url . 'assets/vendor/editor.md/lib/';
			$editormd_localize['plugin_vendor_url']    = $this->githuber_plugin_url . 'assets/vendor/';
			$editormd_localize['editor_placeholder']   = __( 'Happy Markdowning!', 'wp-githuber-md' );
			$editormd_localize['image_paste_callback'] = admin_url( 'admin-ajax.php?action=githuber_image_paste&post_id=' . $post_id . '&_wpnonce=' . wp_create_nonce( 'image_paste_action_' . $post_id ) );
			$editormd_localize['prism_line_number']    = githuber_get_option( 'prism_line_number', 'githuber_modules' );

			// Register JS variables for the Editormd library uses.
			wp_localize_script( 'githuber-md', 'editormd_config', $editormd_localize );
		}

		/* @version 1.6.0 */
		wp_enqueue_script( 'githuber-md-mpp', $this->githuber_plugin_url . 'assets/js/githuber-md-mpp.js', array( 'jquery' ), $this->version, true );

		$metabox_data['ajax_url'] = admin_url( 'admin-ajax.php' );
		$metabox_data['post_id']  = githuber_get_current_post_id();

		wp_localize_script( 'githuber-md-mpp', 'markdown_this_post_config', $metabox_data );
	}

	/**
	 * Initalize to WP `admin_init` hook.
	 */
	public function admin_init() {

		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );

		if ( 'no' !== githuber_get_option( 'markdown_editor_switcher', 'githuber_markdown' ) ) {

			/* @version 1.6.0 */
			add_action( 'wp_ajax_githuber_markdown_this_post', array( $this, 'admin_githuber_markdown_this_post' ) );

			// Add the sidebar metabox to posts.
			$current_post_type = githuber_get_current_post_type();

			// Only display metabox if current post-type supports Markdown.
			if ( ! empty( $current_post_type) && post_type_supports( githuber_get_current_post_type(), self::MD_POST_TYPE ) ) {
				add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
			}
		}
	}

	public function admin_init_meta_box() {

	}

	/**
	 * Markdown parser.
	 *
	 * @return object MarkdownParser instance.
	 */
	public static function get_parser()
	{
		if ( ! self::$parser_instance ) {

			$is_markdown_extra = githuber_get_option( 'support_mardown_extra', 'githuber_extensions' );

			if ( 'yes' === $is_markdown_extra ) {
				self::$parser_instance = new Module\MarkdownExtraParser();
			} else {
				self::$parser_instance = new Module\MarkdownParser();
			}
		}
		return self::$parser_instance;
	}

	/**
	 * Is Markdown conversion for posts or comments enabled?
	 *
	 * @param string $post_action_type The type of posting action.
	 * @return bool
	 */
	public function is_md_enabled( $post_action_type ) {
		switch ( $post_action_type ) {
			case 'posting':
				return true;
				break;
			case 'commeting':
				$setting = githuber_get_option( 'enable_markdown_for_comment', 'githuber_markdown' );
				if ( isset( $setting[ $post_action_type ] ) && $setting[ $post_action_type ] === $post_action_type ) {
					return true;
				}
				break;
		}
		return false;
	}

	/**
	 * Detect the language is defined through the way recommended in the HTML5 draft: through a language-xxxx class.
	 * Find out all of them, then put them into the post meta for frontend uses.
	 *
	 * @param int    $post_id       The post ID.
	 * @param string $post_content The post content.
	 * @return void
	 */
	public function detect_code_languages( $post_id, $post_content ) {

		$prism_meta_array     = array();
		$highlight_meta_array = array();

		delete_metadata( 'post', $post_id, self::MD_POST_META_PRISM);
		delete_metadata( 'post', $post_id, self::MD_POST_META_HIGHLIGHT);
		delete_metadata( 'post', $post_id, self::MD_POST_META_SEQUENCE);
		delete_metadata( 'post', $post_id, self::MD_POST_META_FLOW);

		$is_sequence  = false;
		$is_flowchart = false;
		$is_mermaid   = false;
		$is_katex     = false;
		$is_mathjax   = false;

		if ( preg_match_all( '/<code class="language-([a-z\-0-9]+)"/', $post_content, $matches ) > 0 && ! empty( $matches[1] ) ) {

			foreach ( $matches[1] as $match ) {

				if ( ! empty( Module\Prism::$prism_codes[ $match ] ) ) {
					$prism_meta_array[ $match ] = $match;
				}

				// Check if this componets requires the parent components or not.
				if ( ! empty( Module\Prism::$prism_component_parent[ $match ] ) ) {
					foreach ( Module\Prism::$prism_component_parent[ $match ] as $parent ) {

						// If it need a parent componet, add it to the $paris_meta_array.
						if ( empty( $prism_meta_array[ $parent ] ) ) {
							$prism_meta_array[ $parent ] = $parent;
						}
					}
				}

				if ( ! empty( Module\Highlight::$highlight_codes[ $match ] ) ) {
					$highlight_meta_array[ $match ] = $match;
				}

				if ( 'seq' === $match || 'sequence' === $match ) {
					$is_sequence = true;
				}

				if ( 'flow' === $match || 'flowchart' === $match ) {
					$is_flowchart = true;
				}

				if ( 'mermaid' === $match ) {
					$is_mermaid = true;
				}

				if ( 'katex' === $match ) {
					$is_katex = true;
				}

				if ( 'mathjax' === $match ) {
					$is_mathjax = true;
				}
			}
		} 
		
		// If we find inline KaTex syntax.
		if ( strpos( $post_content, '<code class="katex-inline">' ) !== false ) {
			$is_katex = true;
		}

		// If we find inline MathJax syntax.
		if ( strpos( $post_content, '<code class="mathjax-inline language-mathjax">' ) !== false ) {
			$is_mathjax = true;
		}

		// Combine array into a string.
		$prism_meta_string     = implode( ',', $prism_meta_array );
		$highlight_meta_string = implode( ',', $highlight_meta_array );

		// Store the string to post meta, for identifying what the syntax languages are used in current post.
		if ( $this->is_support_prism && ! empty( $prism_meta_array ) ) {
			update_metadata( 'post', $post_id, self::MD_POST_META_PRISM, $prism_meta_string );
		} else {
			update_metadata( 'post', $post_id, self::MD_POST_META_PRISM, '' );
		}

		if ( $this->is_support_highlight && ! empty( $highlight_meta_array ) ) {
			update_metadata( 'post', $post_id, self::MD_POST_META_HIGHLIGHT, $highlight_meta_string );
		} else {
			update_metadata( 'post', $post_id, self::MD_POST_META_HIGHLIGHT, '' );
		}

		if ( $this->is_support_sequence && $is_sequence ) {
			update_metadata( 'post', $post_id, self::MD_POST_META_SEQUENCE, true );
		} else {
			update_metadata( 'post', $post_id, self::MD_POST_META_SEQUENCE, false );
		}

		if ( $this->is_support_flowchart && $is_flowchart ) {
			update_metadata( 'post', $post_id, self::MD_POST_META_FLOW, true );
		} else {
			update_metadata( 'post', $post_id, self::MD_POST_META_FLOW, false );
		}

		if ( $this->is_support_mermaid && $is_mermaid ) {
			update_metadata( 'post', $post_id, self::MD_POST_META_MERMAID, true );
		} else {
			update_metadata( 'post', $post_id, self::MD_POST_META_MERMAID, false );
		}

		if ( $this->is_support_katex && $is_katex ) {
			update_metadata( 'post', $post_id, self::MD_POST_META_KATEX, true );
		} else {
			update_metadata( 'post', $post_id, self::MD_POST_META_KATEX, false );
		}

		if ( $this->is_support_mathjax && $is_mathjax ) {
			update_metadata( 'post', $post_id, self::MD_POST_META_MATHJAX, true );
		} else {
			update_metadata( 'post', $post_id, self::MD_POST_META_MATHJAX, false );
		}
	}

	/**
	 * Register the `HtmlToMarkdown` meta box in the post-editor.
	 */
	public function add_meta_box() {

		if ( ! githuber_current_user_can( 'edit_posts' ) ) {
			return false;
		}

		add_meta_box(
			'markdown_this_post_meta_box',
			__( 'Enable Markdown', 'wp-githuber-md' ) . '<div class="bg-icon-md"></div>',
			array( $this, 'show_meta_box' ),
			null,
			'side',
			'high'
		);
	}

	/**
	 * Show `HtmlToMarkdown` meta box.
	 */
	public function show_meta_box() {

		$post_id               = githuber_get_current_post_id();
		$markdown_this_post    = get_metadata( 'post', $post_id, self::MD_POST_META_ENABLED, true );

		githuber_logger( 'Show meta box.', array(
			'post_id'            => $post_id,
			'markdown_this_post' => $markdown_this_post,
		) );

		$data['markdown_this_post_choice'] = $markdown_this_post;
		$data['is_markdown_this_post']     = $this->markdown_this_post;

		echo githuber_load_view( 'metabox/markdown-per-post', $data );
	}

	/**
	 * Do action hook for per post Markdown control.
	 */
	public function admin_githuber_markdown_this_post() {

		githuber_logger( 'Start an Ajax call.');

		$response = array(
			'success' => false,
			'result'  => '',
		);

		if ( ! empty( $_POST['post_id'] ) && ! empty( $_POST['markdown_this_post'] ) ) {
			$post_id = (int) $_POST['post_id'];
			$choice  = $_POST['markdown_this_post'];

			if ( 'yes' === $choice ) {
				update_metadata( 'post', $post_id, self::MD_POST_META_ENABLED, 'yes' );
			} else {
				update_metadata( 'post', $post_id, self::MD_POST_META_ENABLED, 'no' );
			}

			$response = array(
				'success' => true,
				'result'  => $choice,
				'post_id' => $post_id,
			);

			githuber_logger( 'Post data is gotten.', array(
				'post_id' => $_POST['post_id'],
				'markdown_this_post' => $_POST['markdown_this_post'],
			) );
		}

		header('Content-type: application/json');

		echo json_encode( $response );

		// To avoid wp_ajax return "0" string to break the vaild json string.
		wp_die();
	}

	/**
	 * The below methods are from Jetpack: Markdown modular
	 * And we modified it for our needs.
	 *
	 * @link https://github.com/Automattic/jetpack/blob/master/modules/markdown/easy-markdown.php
	 * @license GPL
	 */
	public function jetpack_code_snippets() {
		$this->maybe_load_actions_and_filters();

		if ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) {
			add_action( 'switch_blog', array( $this, 'maybe_load_actions_and_filters' ), 10, 2 );
		}
	}

	/**
	 * If we're in a bulk edit session, unload so that we don't lose our markdown metadata
	 */
	public function maybe_unload_for_bulk_edit() {
		if ( isset( $_REQUEST['bulk_edit'] ) && $this->is_md_enabled( 'posting' ) ) {
			$this->unload_markdown_for_posts();
		}
	}

	/**
	 * Called on init and fires on switch_blog to decide if our actions and filters
	 * should be running.
	 * @param int|null $new_blog_id New blog ID
	 * @param int|null $old_blog_id Old blog ID
	 */
	public function maybe_load_actions_and_filters( $new_blog_id = null, $old_blog_id = null ) {

		// If this is a switch_to_blog call, and the blog isn't changing, we'll already be loaded
		if ( $new_blog_id && $new_blog_id === $old_blog_id ) {
			return;
		}
		if ( $this->is_md_enabled( 'posting' ) ) {
			$this->load_markdown( 'posting' );
		} else {
			$this->unload_markdown( 'posting' );
		}
		if ( $this->is_md_enabled( 'commenting' ) ) {
			$this->load_markdown( 'commenting' );
		} else {
			$this->unload_markdown( 'commenting' );
		}
	}

	/**
	 * Set up hooks for enabling Markdown conversion on specfic post action.
	 *
	 * @param $post_action_type posting|commenting
	 * @return void
	 */
	public function load_markdown( $post_action_type ) {
		switch ( $post_action_type ) {
			case 'posting':
				// Set up hooks for enabling Markdown conversion on posts
				add_action( 'wp_insert_post', array( $this, 'wp_insert_post' ) );
				add_filter( 'wp_insert_post_data', array( $this, 'wp_insert_post_data' ), 10, 2 );
				add_filter( 'edit_post_content', array( $this, 'edit_post_content' ), 10, 2 );
				add_filter( 'edit_post_content_filtered', array( $this, 'edit_post_content_filtered' ), 10, 2 );
				add_action( 'wp_restore_post_revision', array( $this, 'wp_restore_post_revision' ), 10, 2 );
				add_filter( '_wp_post_revision_fields', array( $this, '_wp_post_revision_fields' ) );
				add_action( 'xmlrpc_call', array( $this, 'xmlrpc_actions' ) );
				add_filter( 'content_save_pre', array( $this, 'preserve_code_blocks' ), 1 );

				if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
					$this->check_for_early_methods();
				}
				break;
			case 'commenting':
				// Use priority 9 so that Markdown runs before KSES, which can clean up any munged HTML.
				add_filter( 'pre_comment_content', array( $this, 'pre_comment_content' ), 9 );
				break;
			default:
		}
	}

	/**
	 * Removes hooks to disable Markdown conversion on specfic post action.
	 *
	 * @param $post_action_type posting|commenting
	 * @return void
	 */
	public function unload_markdown( $post_action_type ) {
		switch ( $post_action_type ) {
			case 'posting':
				remove_action( 'wp_insert_post', array( $this, 'wp_insert_post' ) );
				remove_filter( 'wp_insert_post_data', array( $this, 'wp_insert_post_data' ), 10, 2 );
				remove_filter( 'edit_post_content', array( $this, 'edit_post_content' ), 10, 2 );
				remove_filter( 'edit_post_content_filtered', array( $this, 'edit_post_content_filtered' ), 10, 2 );
				remove_action( 'wp_restore_post_revision', array( $this, 'wp_restore_post_revision' ), 10, 2 );
				remove_filter( '_wp_post_revision_fields', array( $this, '_wp_post_revision_fields' ) );
				remove_action( 'xmlrpc_call', array( $this, 'xmlrpc_actions' ) );
				remove_filter( 'content_save_pre', array( $this, 'preserve_code_blocks' ), 1 );
				break;
			case 'commenting':
				remove_filter( 'pre_comment_content', array( $this, 'pre_comment_content' ), 9 );
				break;
			default:
		}
	}

	/**
	 * Sanitize setting. Don't really want to store "on" value, so we'll store "1" instead!
	 * @param  string $input Value received by settings API via $_POST
	 * @return bool   Cast to boolean.
	 */
	public function sanitize_setting( $input ) {
		return (bool) $input;
	}

	/**
	 * Check if a $post_id has Markdown enabled
	 * @param  int  $post_id A post ID.
	 * @return bool
	 */
	public function has_markdown( $post_id ) {
		if ( get_metadata( 'post', $post_id, self::MD_POST_META, true ) ) {
			return true;
		}

		// Backward check Jetpack Markdown.
		if ( get_metadata( 'post', $post_id, self::JETPACK_MD_POST_META, true ) ) {
			return true;
		}
		return false;
	}

	/**
	 * Set Markdown as enabled on a post_id. We skip over update_postmeta so we
	 * can sneakily set metadata on post revisions, which we need.
	 * @param int    $post_id A post ID.
	 * @return bool  The metadata was successfully set.
	 */
	protected function set_as_markdown( $post_id ) {
		return update_metadata( 'post', $post_id, self::MD_POST_META, true );
	}

	/**
	 * Swap post_content and post_content_filtered for editing
	 * @param  string $content Post content
	 * @param  int $id         post ID
	 * @return string          Swapped content
	 */
	public function edit_post_content( $content, $id ) {
		if ( $this->has_markdown( $id ) ) {
			$post = get_post( $id );
			if ( $post && ! empty( $post->post_content_filtered ) ) {
				$post = $this->swap_for_editing( $post );
				return $post->post_content;
			}
		}
		return $content;
	}

	/**
	 * Swap post_content_filtered and post_content for editing
	 * @param  string $content Post content_filtered
	 * @param  int    $id      post ID
	 * @return string          Swapped content
	 */
	public function edit_post_content_filtered( $content, $id ) {

		// if markdown was disabled, let's turn this off
		if ( ! $this->is_md_enabled( 'posting' ) && $this->has_markdown( $id ) ) {
			$post = get_post( $id );
			if ( $post && ! empty( $post->post_content_filtered ) ) {
				$content = '';
			}
		}
		return $content;
	}

	/**
	 * Magic happens here. Markdown is converted and stored on post_content. Original Markdown is stored
	 * in post_content_filtered so that we can continue editing as Markdown.
	 *
	 * @param  array $post_data The post data that will be inserted into the DB. Slashed.
	 * @param  array $postarr   All the stuff that was in $_POST.
	 * @return array            $post_data with post_content and post_content_filtered modified
	 */
	public function wp_insert_post_data( $post_data, $postarr ) {

		// $post_data array is slashed!
		$post_id = isset( $postarr['ID'] ) ? $postarr['ID'] : false;

		// bail early if markdown is disabled or this post type is unsupported.
		if ( ! $this->is_md_enabled( 'posting' ) || ! post_type_supports( $post_data['post_type'], self::MD_POST_TYPE ) ) {

			// it's disabled, but maybe this *was* a markdown post before.
			if ( $this->has_markdown( $post_id ) && ! empty( $post_data['post_content_filtered'] ) ) {
				$post_data['post_content_filtered'] = '';
			}

			// we have no context to determine supported post types in the `post_content_pre` hook,
			// which already ran to sanitize code blocks. Undo that.
			$post_data['post_content'] = $this->restore_code_blocks( $post_data['post_content'] );
			return $post_data;
		}

		// rejigger post_content and post_content_filtered
		// revisions are already in the right place, except when we're restoring, but that's taken care of elsewhere
		// also prevent quick edit feature from overriding already-saved markdown (issue https://github.com/Automattic/jetpack/issues/636)
		if ( 'revision' !== $post_data['post_type'] && ! isset( $_POST['_inline_edit'] ) ) {

			$post_data['post_content_filtered'] = $post_data['post_content'];
			$post_data['post_content'] = $this->transform( $post_data['post_content'], array( 'id' => $post_id ) );

			if ( $this->is_convert_remote_image() ) {
				foreach ( FetchRemoteImage::$image_list as $image_info ) {
					$post_data['post_content_filtered'] = str_replace( $image_info['before'], $image_info['after'], $post_data['post_content_filtered'] );
				}
			}

			/** This filter is already documented in core/wp-includes/default-filters.php */
			$post_data['post_content'] = apply_filters( 'content_save_pre', $post_data['post_content'] );

		} elseif ( 0 === strpos( $post_data['post_name'], $post_data['post_parent'] . '-autosave' ) ) {

			// autosaves for previews are weird
			$post_data['post_content_filtered'] = $post_data['post_content'];
			$post_data['post_content'] = $this->transform( $post_data['post_content'], array( 'id' => $post_data['post_parent'] ) );

			if ( $this->is_convert_remote_image() ) {
				foreach ( FetchRemoteImage::$image_list as $image_info ) {
					$post_data['post_content_filtered'] = str_replace( $image_info['before'], $image_info['after'], $post_data['post_content_filtered'] );
				}
			}

			/** This filter is already documented in core/wp-includes/default-filters.php */
			$post_data['post_content'] = apply_filters( 'content_save_pre', $post_data['post_content'] );
		}

		// set as markdown on the wp_insert_post hook later
		if ( $post_id ) {
			$this->monitoring['post'][ $post_id ] = true;
		} else {
			$this->monitoring['content'] = wp_unslash( $post_data['post_content'] );
		}

		if ( 'revision' === $postarr['post_type'] && $this->has_markdown( $postarr['post_parent'] ) ) {
			$this->monitoring['parent'][ $postarr['post_parent'] ] = true;
		}
	
		// Is it support Prism - syntax highlighter.
		$this->detect_code_languages( $post_id, wp_unslash( $post_data['post_content'] ) );

		$post_data['post_content'] = $this->fix_issue_209( $post_data['post_content'] );

		return $post_data;
	}

	/**
	 * Calls on wp_insert_post action, after wp_insert_post_data. This way we can
	 * still set postmeta on our revisions after it's all been deleted.
	 * @param  int $post_id The post ID that has just been added/updated
	 * @return null
	 */
	public function wp_insert_post( $post_id ) {
		$post_parent = get_post_field( 'post_parent', $post_id );
		// this didn't have an ID yet. Compare the content that was just saved.
		if ( isset( $this->monitoring['content'] ) && $this->monitoring['content'] === get_post_field( 'post_content', $post_id ) ) {
			unset( $this->monitoring['content'] );
			$this->set_as_markdown( $post_id );
		}
		if ( isset( $this->monitoring['post'][ $post_id ] ) ) {
			unset( $this->monitoring['post'][ $post_id ] );
			$this->set_as_markdown( $post_id );
		} elseif ( isset( $this->monitoring['parent'][ $post_parent ] ) ) {
			unset( $this->monitoring['parent'][ $post_parent ] );
			$this->set_as_markdown( $post_id );
		}
	}

	/**
	 * Run a comment through Markdown. Easy peasy.
	 *
	 * @param  string $content
	 * @return string
	 */
	public function pre_comment_content( $content ) {
		return $this->transform( $content, array(
			'id' => $this->comment_hash( $content ),
		) );
	}
	protected function comment_hash( $content ) {
		return 'c-' . substr( md5( $content ), 0, 8 );
	}

	/**
	 * Markdown conversion. Some DRYness for repetitive tasks.
	 *
	 * @param string $text Content to be run through Markdown
	 * @param array  $args Arguments, with keys:
	 *                     id: provide a string to prefix footnotes with a unique identifier
	 *                     unslash: when true, expects and returns slashed data
	 *                     decode_code_blocks: when true, assume that text in fenced code blocks is already
	 *                     HTML encoded and should be decoded before being passed to Markdown, which does
	 *                     its own encoding.
	 * @return string Markdown-processed content
	 */
	public function transform( $text, $args = array() ) {

		$is_decode_code_blocks = ( 'yes' === githuber_get_option( 'decode_code_blocks', 'githuber_preferences' ) ) ? true : false;

		$args = wp_parse_args( $args, array(
			'id'                 => false,
			'unslash'            => true,
			'decode_code_blocks' => $is_decode_code_blocks, // Fix: issue #30
			//'decode_code_blocks' => false, // Fix: issue #30
		) );

		// probably need to unslash
		if ( $args['unslash'] ) {
			$text = wp_unslash( $text );
		}

		// ensure our paragraphs are separated
		$text = str_replace( array( '</p><p>', "</p>\n<p>" ), "</p>\n\n<p>", $text );

		// visual editor likes to add <p>s. Buh-bye.
		$text = $this->get_parser()->remove_bare_p_tags( $text );

		// sometimes we get an encoded > at start of line, breaking blockquotes
		$text = preg_replace( '/^&gt;/m', '>', $text );

		// If we're not using the code shortcode, prevent over-encoding.
		if ( $args['decode_code_blocks'] ) {
			$text = $this->restore_code_blocks( $text );
		}

		// Transform it!
		$text = $this->get_parser()->transform( $text );

		// Fetch remote images.
		if ( $this->is_convert_remote_image() ) {
			$text = $this->convert_remote_image( $text );
		}

		// Render Github Flavored Markdown task lists if this module is enabled.
		if ( $this->is_support_task_list ) {
			$text = Module\TaskList::parse_gfm_task_list( $text );
		}

		// Render KaTeX inline markup.
		if ( $this->is_support_katex ) {
			$text = Module\KaTeX::katex_inline_markup( $text );
		}

		// Render MathJax inline markup.
		if ( $this->is_support_mathjax ) {
			$text = Module\MathJax::mathjax_inline_markup( $text );
		}

		// Markdown inserts extra spaces to make itself work. Buh-bye.
		$text = rtrim( $text );

		// probably need to re-slash
		if ( $args['unslash'] ) {
			$text = wp_slash( $text );
		}

		return $text;
	}

	/**
	 * Shows Markdown in the Revisions screen, and ensures that post_content_filtered
	 * is maintained on revisions
	 *
	 * @param  array $fields Post fields pertinent to revisions
	 * @return array         Modified array to include post_content_filtered
	 */
	public function _wp_post_revision_fields( $fields ) {
		$fields['post_content_filtered'] = __( 'Markdown content', 'jetpack' );
		return $fields;
	}

	/**
	 * Do some song and dance to keep all post_content and post_content_filtered content
	 * in the expected place when a post revision is restored.
	 *
	 * @param  int $post_id        The post ID have a restore done to it
	 * @param  int $revision_id    The revision ID being restored
	 */
	public function wp_restore_post_revision( $post_id, $revision_id ) {
		if ( $this->has_markdown( $revision_id ) ) {
			$revision = get_post( $revision_id, ARRAY_A );
			$post = get_post( $post_id, ARRAY_A );

			// Yes, we put it in post_content, because our wp_insert_post_data() expects that
			$post['post_content'] = $revision['post_content_filtered'];

			// set this flag so we can restore the post_content_filtered on the last revision later
			$this->monitoring['restore'] = true;

			// let's not make a revision of our fixing update
			add_filter( 'wp_revisions_to_keep', '__return_false', 99 );
			wp_update_post( $post );
			$this->fix_latest_revision_on_restore( $post_id );
			remove_filter( 'wp_revisions_to_keep', '__return_false', 99 );
		}
	}

	/**
	 * We need to ensure the last revision has Markdown, not HTML in its post_content_filtered
	 * column after a restore.
	 *
	 * @param int $post_id The post ID that was just restored.
	 */
	protected function fix_latest_revision_on_restore( $post_id ) {
		$post = get_post( $post_id );
		$last_revision = self::$model_instance->get_lastest_revision( $post->ID );
		$last_revision->post_content_filtered = $post->post_content_filtered;
		wp_insert_post( (array) $last_revision );
	}

	/**
	 * Kicks off magic for an XML-RPC session. We want to keep editing Markdown
	 * and publishing HTML.
	 *
	 * @param  string $xmlrpc_method The current XML-RPC method
	 * @return void
	 */
	public function xmlrpc_actions( $xmlrpc_method ) {
		switch ( $xmlrpc_method ) {
			case 'metaWeblog.getRecentPosts':
			case 'wp.getPosts':
			case 'wp.getPages':
				add_action( 'parse_query', array( $this, 'make_filterable' ), 10, 1 );
				break;
			case 'wp.getPost':
				$this->prime_post_cache();
				break;
		}
	}

	/**
	 * metaWeblog.getPost and wp.getPage fire xmlrpc_call action *after* get_post() is called.
	 * So, we have to detect those methods and prime the post cache early.
	 */
	protected function check_for_early_methods() {
		$raw_post_data = file_get_contents( "php://input" );
		if ( false === strpos( $raw_post_data, 'metaWeblog.getPost' )
			&& false === strpos( $raw_post_data, 'wp.getPage' ) ) {
			return;
		}
		include_once( ABSPATH . WPINC . '/class-IXR.php' );
		$message = new \IXR_Message( $raw_post_data );
		$message->parse();
		$post_id_position = 'metaWeblog.getPost' === $message->methodName ? 0 : 1;
		$this->prime_post_cache( $message->params[ $post_id_position ] );
	}

	/**
	 * Prime the post cache with swapped post_content. This is a sneaky way of getting around
	 * the fact that there are no good hooks to call on the *.getPost xmlrpc methods.
	 */
	private function prime_post_cache( $post_id = false ) {
		global $wp_xmlrpc_server;
		if ( ! $post_id ) {
			$post_id = $wp_xmlrpc_server->message->params[3];
		}
		// prime the post cache
		if ( $this->has_markdown( $post_id ) ) {
			$post = get_post( $post_id );
			if ( ! empty( $post->post_content_filtered ) ) {
				wp_cache_delete( $post->ID, 'posts' );
				$post = $this->swap_for_editing( $post );
				wp_cache_add( $post->ID, $post, 'posts' );
				$this->posts_to_uncache[] = $post_id;
			}
		}
		// uncache munged posts if using a persistent object cache
		if ( wp_using_ext_object_cache() ) {
			add_action( 'shutdown', array( $this, 'uncache_munged_posts' ) );
		}
	}

	/**
	 * Swaps `post_content_filtered` back to `post_content` for editing purposes.
	 *
	 * @param  object $post WP_Post object
	 * @return object       WP_Post object with swapped `post_content_filtered` and `post_content`
	 */
	protected function swap_for_editing( $post ) {
		$markdown = $post->post_content_filtered;

		// unencode encoded code blocks
		$markdown = $this->restore_code_blocks( $markdown );

		// restore beginning of line blockquotes
		$markdown = preg_replace( '/^&gt; /m', '> ', $markdown );
		$post->post_content_filtered = $post->post_content;
		$post->post_content = $markdown;
		return $post;
	}

	/**
	 * We munge the post cache to serve proper markdown content to XML-RPC clients.
	 * Uncache these after the XML-RPC session ends.
	 */
	public function uncache_munged_posts() {
		// $this context gets lost in testing sometimes. Weird.
		foreach ( $this->posts_to_uncache as $post_id ) {
			wp_cache_delete( $post_id, 'posts' );
		}
	}

	/**
	 * Since *.(get)?[Rr]ecentPosts calls get_posts with suppress filters on, we need to
	 * turn them back on so that we can swap things for editing.
	 *
	 * @param  object $wp_query WP_Query object
	 */
	public function make_filterable( $wp_query ) {
		$wp_query->set( 'suppress_filters', false );
		add_action( 'the_posts', array( $this, 'the_posts' ), 10, 2 );
	}

	/**
	 * Swaps post_content and post_content_filtered for editing.
	 *
	 * @param  array  $posts    Posts returned by the just-completed query
	 * @param  object $wp_query Current WP_Query object
	 * @return array            Modified $posts
	 */
	public function the_posts( $posts, $wp_query ) {
		foreach ( $posts as $key => $post ) {
			if ( $this->has_markdown( $post->ID ) && ! empty( $posts[ $key ]->post_content_filtered ) ) {
				$markdown = $posts[ $key ]->post_content_filtered;
				$posts[ $key ]->post_content_filtered = $posts[ $key ]->post_content;
				$posts[ $key ]->post_content = $markdown;
			}
		}
		return $posts;
	}

	/**
	 * Preserve code blocks from being munged by KSES before they have a chance
	 *
	 * @param  string $text post content
	 * @return string       post content with code blocks escaped
	 */
	public function preserve_code_blocks( $text ) {
		return $this->get_parser()->codeblock_preserve( $text );
	}

	/**
	 * Restore code blocks.
	 *
	 * @param  string $text post content
	 * @return string       post content with code blocks unescaped
	 */
	public function restore_code_blocks( $text ) {
		$text = $this->get_parser()->codeblock_restore( $text );
		return $this->fix_issue_209( $text );
	}

	/**
	 * https://github.com/terrylinooo/githuber-md/issues/209
	 *
	 * @param  string $text post content
	 * @return string       post content with code blocks unescaped
	 */
	public function fix_issue_209( $text ) {
		// Use a unique string `_!_!_` to replace `&#`, then covert it to `&amp;#`
		$text = str_replace( '_!_!_', '&amp;#', $text );
		return $text;
	}

	/**
	 * Force-disable Jetpack's Markdown module if it is active.
	 *
	 * @param array $modules Array of active Jetpack modules.
	 *
	 * @return array $modules Array of active Jetpack modules.
	 */
	public function admin_githuber_disable_jetpack_markdown( $modules ) {
		$found = array_search( 'markdown', $modules, true );
		if ( false !== $found ) {
			unset( $modules[ $found ] );
		}
		return $modules;
	}

	/**
	 * Detect remote images.
	 *
	 * @param string $post_content 
	 * 
	 * @return string
	 */
	public function convert_remote_image( $post_content  ) {

		if ( $this->is_convert_remote_image() ) {
			$post_content  = FetchRemoteImage::covert( $post_content  );
		}
		return $post_content ;
	}

	/**
	 * Is performing covert remote image.
	 *
	 * @return bool
	 */
	public function is_convert_remote_image() {
		if ( 'yes' === githuber_get_option( 'fetch_remote_image', 'githuber_markdown' ) ) {
			if ( isset( $_POST['fetch_remote_image'] ) && 'yes' === $_POST['fetch_remote_image'] ) {
				return true;
			}	
		}
		return false;
	}

}
Controllers/Setting.php000066600000130022151747671010011213 0ustar00<?php
/**
 * Class Setting
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.7.0
 */

namespace Githuber\Controller;

class Setting extends ControllerAbstract {

	public static $settings = array();
	public static $setting_api;

	/**
	 * Where the Githuber MD's setting menu displays on.
	 *
	 * @var string
	 */
	public $menu_position = 'options';

	/**
	 * Menu slug.
	 *
	 * @var string
	 */
	public $menu_slug = 'githuber-md';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();

		if ( ! self::$setting_api ) {
			self::$setting_api = new \Githuber_Settings_API();
		}
	}

	/**
	 * Initialize.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'setting_admin_init' ) );
		add_action( 'admin_menu', array( $this, 'setting_admin_menu' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
		add_filter( 'plugin_action_links_' . $this->githuber_plugin_name, array( $this, 'plugin_action_links' ), 10, 5 );
		add_filter( 'plugin_row_meta', array( $this, 'plugin_extend_links' ), 10, 2 );
	}

	/**
	 * Load specfic CSS file for the Githuber setting page.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {

		if ( false === strpos( $hook_suffix, 'githuber-md' ) ) {
			return;
		}
		//wp_enqueue_style( 'custom_wp_admin_css', $this->githuber_plugin_url . 'assets/css/admin-style.css', array(), $this->version, 'all' );
	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {

	}

	/**
	 * The Githuber setting page, sections and fields.
	 */
	public function setting_admin_init() {

		// set sections and fields.
		self::$setting_api->set_sections( $this->get_sections() );

		$settings = $this->get_fields();

		if ( $GLOBALS['wp_version'] < '4.5' ) {
			// sequence_diagram uses underscore.js, and it has some conflict issues with WordPress's plupload uploader in older vision.
			// So, we hide this option in older version.
			foreach ( $settings['githuber_markdown'] as $k => $v ) {
				if ( 'support_sequence_diagram' === $v['name'] ) {
					unset( $settings['githuber_markdown'][ $k ] );
				}
			}
			foreach ( $settings['githuber_modules'] as $k => $v ) {
				if ( 'sequence_diagram_src' === $v['name'] ) {
					unset( $settings['githuber_modules'][ $k-1 ] );
					unset( $settings['githuber_modules'][ $k ] );
				}
			}
		}

		self::$setting_api->set_fields( $settings );

		// initialize them.
		self::$setting_api->admin_init();

		self::$settings = $settings;
	}

	/**
	 * Setting sections.
	 *
	 * @return array
	 */
	public function get_sections() {

		return array(

			array(
				'id'    => 'githuber_markdown',
				'title' => __( 'Markdown', 'wp-githuber-md' ),
			),

			array(
				'id'    => 'githuber_modules',
				'title' => __( 'Modules', 'wp-githuber-md' ),
			),

			array(
				'id'    => 'githuber_extensions',
				'title' => __( 'Extensions', 'wp-githuber-md' ),
			),

			array(
				'id'    => 'githuber_preferences',
				'title' => __( 'Preferences', 'wp-githuber-md' ),
			),

			array(
				'id'    => 'githuber_about',
				'title' => __( 'About', 'wp-githuber-md' ),
			),
		);
	}

	/**
	 * Setting fields.
	 *
	 * @return array
	 */
	public function get_fields() {

		$support_post_types = get_post_types( array( 'public' => true ), 'objects' );

		$post_type_options = array();

		foreach($support_post_types as $post_type) {
			if( 'attachment' !== $post_type->name ) {
				$post_type_options[ $post_type->name ] = $post_type->label;
			}
		}

		$system_lang             = get_locale();
		$default_spellcheck_lang = 'en_GB';
		$spellcheck_warning      = '';

		$spellcheck_lang_list = array(
			'af_ZA' => 'af_ZA',
			'bg_BG' => 'bg_BG',
			'ca_ES' => 'ca_ES',
			'cs_CZ' => 'cs_CZ',
			'cy_GB' => 'cy_GB',
			'da_DK' => 'da_DK',
			'de_DE' => 'de_DE',
			'el_GR' => 'el_GR',
			'en_AU' => 'en_AU',
			'en_CA' => 'en_CA',
			'en_GB' => 'en_GB',
			'en_US' => 'en_US',
			'es_ES' => 'es_ES',
			'et_EE' => 'et_EE',
			'fa_IR' => 'fa_IR',
			'fr_FR' => 'fr_FR',
			'he_IL' => 'he_IL',
			'hi_IN' => 'hi_IN',
			'hr_HR' => 'hr_HR',
			'hu_HU' => 'hu_HU',
			'hy'    => 'hy',
			'id_ID' => 'id_ID',
			'it_IT' => 'it_IT',
			'ko'    => 'ko',
			'lt_LT' => 'lt_LT',
			'lv_LV' => 'lv_LV',
			'nb_NO' => 'nb_NO',
			'nl_NL' => 'nl_NL',
			'pl_PL' => 'pl_PL',
			'pt_BR' => 'pt_BR',
			'pt_PT' => 'pt_PT',
			'ro_RO' => 'ro_RO',
			'ru_RU' => 'ru_RU',
			'sh'    => 'sh',
			'sk_SK' => 'sk_SK',
			'sl_SL' => 'sl_SL',
			'sq'    => 'sq',
			'sr'    => 'sr',
			'sv_SE' => 'sv_SE',
			'ta_IN' => 'ta_IN',
			'tg_TG' => 'tg_TG',
			'tr'    => 'tr',
			'uk_UA' => 'uk_UA',
			'vi_VI' => 'vi_VI',
			'vi_VN' => 'vi_VN',
		);

		if ( array_key_exists( $system_lang, $spellcheck_lang_list ) ) {
			$default_spellcheck_lang = $system_lang;
			$spellcheck_warning      = '<br /><span style="color: #0081ab">' . __( 'Your system langauge is supported.', 'wp-githuber-md' ) . ' (' . $system_lang . ')</span>';
		} else {
			$spellcheck_warning = '<br /><span style="color: #b00000">' . __( 'Your system langauge is not supported.', 'wp-githuber-md' ) . ' (' . $system_lang . ')</span>';
		}

		return array(

			'githuber_markdown' => array(

				array(
					'section_title' => true,
					'label' => __( 'Writing', 'wp-githuber-md' ),
				),

				array(
					'name'    => 'enable_markdown_for_post_types',
					'label'   => __( 'Enable', 'wp-githuber-md' ),
					'desc'    => __( 'Which post types you would like to enable Markdown editor for.', 'wp-githuber-md' ),
					'type'    => 'multicheck',
					'options' => $post_type_options,
					'default' => array(
						'post' => 'post',
						'page' => 'page',
					)
				),

				/*

				array(
					'name'    => 'enable_markdown_for_comment',
					'label'   => '',
					'desc'    => __( 'Enable Markdown for comments.', 'wp-githuber-md' ),
					'type'    => 'multicheck',
					'options' => array(
						'commenting' => __( 'Comments', 'wp-githuber-md' )
					)
				),

				*/

				array(
					'name'    => 'disable_revision',
					'label'   => __( 'Disable Revision', 'wp-githuber-md' ),
					'desc'    => __( 'If you think the revision function is annoying when you\'re writing, you can to disable it.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no',
				),

				array(
					'name'    => 'disable_autosave',
					'class'   => 'disable_autosave',
					'label'   => __( 'Disable Auto-save', 'wp-githuber-md' ),
					'desc'    => __( 'If you think the auto-save function is annoying when you\'re writing, you can to disable it.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
				),

				array(
					'name'    => 'editor_spell_check',
					'label'   => __( 'Spell Check', 'wp-githuber-md' ),
					'desc'    => __( 'Enable spell check on the input. (This feature does not apply to code blocks.)', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no',
				),

				array(
					'name'    => 'editor_spell_check_lang',
					'label'   => __( 'Language', 'wp-githuber-md' ),
					'desc'    => __( 'Please specify your language for spell check in the setting above.', 'wp-githuber-md' ) . $spellcheck_warning,
					'type'    => 'select',
					'default' => $default_spellcheck_lang,
					'options' => $spellcheck_lang_list,
				),

				array(
					'name'    => 'editor_match_highlighter',
					'label'   => __( 'Match Highlighter', 'wp-githuber-md' ),
					'desc'    => __( 'Everywhere else in your text where <strong>current word</strong> appears will automatically illuminate.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'default' => 'no',
				),

				array(
					'section_title' => true,
					'label' => __( 'Meta Boxes', 'wp-githuber-md' ),
				),

				array(
					'name'    => 'html_to_markdown',
					'class'   => 'html_to_markdown',
					'label'   => __( 'HTML-to-Markdown', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'setting/html-to-markdown' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
				),

				array(
					'name'    => 'markdown_editor_switcher',
					'class'   => 'markdown_editor_switcher',
					'label'   => __( 'Markdown Editor Switcher', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'setting/markdown-editor-switcher' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
				),

				array(
					'name'    => 'fetch_remote_image',
					'class'   => 'fetch_remote_image',
					'label'   => __( 'Fetch Remote Image', 'wp-githuber-md' ),
					'desc'    => __( 'A remote image means that it is not a URL from your site. This option allows you to fetch remote images and save them into local folder.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no',
				),

				array(
					'name'    => 'keyword_suggestion_tool',
					'class'   => 'keyword_suggestion_tool',
					'label'   => __( 'Keyword Suggestion Tool', 'wp-githuber-md' ),
					'desc'    => __( 'This keyword suggestion tool can give you a list of long-tail terms based on the keyword you enter. If you are good in On-page SEO skills, it will help you a lot in writing. Data source is from Google Suggestions.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no',
				),

				array(
					'section_title' => true,
					'label' => __( 'Markdown Editor', 'wp-githuber-md' ),
				),

				array(
					'name'    => 'editor_live_preview',
					'label'   => __( 'Live Preview', 'wp-githuber-md' ),
					'desc'    => __( 'Split editor into two panes to display a live preview when editing post.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
				),

				array(
					'name'    => 'editor_sync_scrolling',
					'label'   => __( 'Sync Scrolling', 'wp-githuber-md' ),
					'desc'    => __( 'Synchronize scrolling of two editor panes by content.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
				),

				array(
					'name'    => 'editor_html_decode',
					'label'   => __( 'HTML Decode', 'wp-githuber-md' ),
					'desc'    => __( 'Allow all HTML tags and attributes in the Markdown Editor.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
				),

				array(
					'name'    => 'editor_line_number',
					'label'   => __( 'Line Number', 'wp-githuber-md' ),
					'desc'    => __( 'Display line number in the Markdown Editor.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
				),

				array(
					'name'    => 'editor_toolbar_theme',
					'label'   => __( 'Toolbar', 'wp-githuber-md' ),
					'desc'    => __( 'Choose a perferred style for the Editor\'s toolbar.', 'wp-githuber-md' ),
					'type'    => 'select',
					'default' => 'default',
					'options' => array(
						'default' => 'default',
						'dark'    => 'dark',
					),
				),

				array(
					'name'    => 'editor_editor_theme',
					'label'   => __( 'Editing Area', 'wp-githuber-md' ),
					'desc'    => __( 'Choose a perferred style for the Editor\'s editing area.', 'wp-githuber-md' ),
					'type'    => 'select',
					'default' => 'default',
					'options' => array(
						'default'                 => 'default',
						'3024-day'                => '3024-day',
						'3024-night'              => '3024-night',
						'abcdef'                  => 'abcdef',
						'ambiance'                => 'ambiance',
						'ambiance-mobile'         => 'ambiance-mobile',
						'base16-dark'             => 'base16-dark',
						'base16-light'            => 'base16-light',
						'bespin'                  => 'bespin',
						'blackboard'              => 'blackboard',
						'cobalt'                  => 'cobalt',
						'colorforth'              => 'colorforth',
						'dracula'                 => 'dracula',
						'duotone-dark'            => 'duotone-dark',
						'duotone-light'           => 'duotone-light',
						'eclipse'                 => 'eclipse',
						'elegant'                 => 'elegant',
						'erlang-dark'             => 'erlang-dark',
						'gruvbox-dark'            => 'gruvbox-dark',
						'hopscotch'               => 'hopscotch',
						'icecoder'                => 'icecoder',
						'idea'                    => 'idea',
						'isotope'                 => 'isotope',
						'lesser-dark'             => 'lesser-dark',
						'liquibyte'               => 'liquibyte',
						'lucario'                 => 'lucario',
						'material'                => 'material',
						'mbo'                     => 'mbo',
						'mdn-like'                => 'mdn-like',
						'midnight'                => 'midnight',
						'monokai'                 => 'monokai',
						'neat'                    => 'neat',
						'neo'                     => 'neo',
						'night'                   => 'night',
						'oceanic-next'            => 'oceanic-next',
						'panda-syntax'            => 'panda-syntax',
						'paraiso-dark'            => 'paraiso-dark',
						'paraiso-light'           => 'paraiso-light',
						'pastel-on-dark'          => 'pastel-on-dark',
						'railscasts'              => 'railscasts',
						'rubyblue'                => 'rubyblue',
						'seti'                    => 'seti',
						'shadowfox'               => 'shadowfox',
						'solarized'               => 'solarized',
						'ssms'                    => 'ssms',
						'the-matrix'              => 'the-matrix',
						'tomorrow-night-bright'   => 'tomorrow-night-bright',
						'tomorrow-night-eighties' => 'tomorrow-night-eighties',
						'ttcn'                    => 'ttcn',
						'twilight'                => 'twilight',
						'vibrant-ink'             => 'vibrant-ink',
						'xq-dark'                 => 'xq-dark',
						'xq-light'                => 'xq-light',
						'yeti'                    => 'yeti',
						'zenburn'                 => 'zenburn',
					),
				),

				/*

				array(
					'name'    => 'support_toc',
					'label'   => __( 'Table of Content', 'wp-githuber-md' ),
					'desc'    => __( 'Display a TOC in the every first section.', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'no',
					'options' => array(
						'yes' => __( 'Yes', 'wp-githuber-md' ),
						'no'  => __( 'No', 'wp-githuber-md' )
					)
				),

				array(
					'name'    => 'support_emoji',
					'label'   => __( 'Emoji', 'wp-githuber-md' ),
					'desc'    => __( 'Support Emoji in posts.', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'no',
					'options' => array(
						'yes' => __( 'Yes', 'wp-githuber-md' ),
						'no'  => __( 'No', 'wp-githuber-md' )
					)
				),

				*/


			),

			'githuber_modules' =>  array(

				array(
					'label'         => __( 'Syntax Highlight', 'wp-githuber-md' ),
					'section_title' => true,
					'location_id'   => 'syntax-highlight',
					'desc'          => __( 'prism.js', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_prism',
					//'label'     => __( 'Syntax Highlight', 'wp-githuber-md' ),
					'desc'        => __( 'Highligh the syntax in your code snippets by Prism.js', 'wp-githuber-md' ) . '<br />' . __( 'This option is not available if you choose another highlighter modules.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'syntax-highlight',
					'default'     => 'no',
				),

				array(
					'name'    => 'prism_theme',
					'label'   => __( 'Theme', 'wp-githuber-md' ),
					'desc'    => __( 'Choose a perferred theme for the syntax highlighter.', 'wp-githuber-md' ),
					'type'    => 'select',
					'default' => 'default',
					'parent'  => 'support_prism',
					'options' => array(
						'default'        => 'default',
						'dark'           => 'dark',
						'funky'          => 'funky',
						'okaidia'        => 'okaidia',
						'twilight'       => 'twilight',
						'tomorrow'       => 'tomorrow',
						'coy'            => 'coy',
						'solarizedlight' => 'solarizedlight',
					),
				),

				array(
					'name'    => 'prism_line_number',
					'label'   => __( 'Line Number', 'wp-githuber-md' ),
					'desc'    => __( 'Show line number in code area?', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no',
					'parent'  => 'support_prism',
				),

				array(
					'name'    => 'prism_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_prism',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
					)
				),

				array(
					'label'   => __( 'Example', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/prism' ),
					'type'    => 'html',
					'parent'  => 'support_prism',
				),

				// -------------------------------------------------------------------//

				array(
					'label'         => __( 'Syntax Highlight', 'wp-githuber-md' ),
					'section_title' => true,
					'location_id'   => 'syntax-highlight-js',
					'desc'          => __( 'highlight.js', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_highlight',
					//'label'     => __( 'Syntax Highlight', 'wp-githuber-md' ),
					'desc'        => __( 'Highligh the syntax in your code snippets by Highlight.js', 'wp-githuber-md' ) . '<br />' . __( 'This option is not available if you choose another highlighter modules.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'syntax-highlight-js',
					'default'     => 'no',
				),

				array(
					'name'    => 'highlight_theme',
					'label'   => __( 'Theme', 'wp-githuber-md' ),
					'desc'    => __( 'Choose a perferred theme for the syntax highlighter.', 'wp-githuber-md' ) . ' [<a href="https://highlightjs.org/static/demo/" target="_blank">' . __( 'Demo') . '</a>]',
					'type'    => 'select',
					'default' => 'default',
					'parent'  => 'support_highlight',
					'options' => array(
						'default'                   => 'Default',
						'a11y-dark'                 => 'A 11 Y Dark',
						'a11y-light'                => 'A 11 Y Light',
						'agate'                     => 'Agate',
						'an-old-hope'               => 'An Old Hope',
						'androidstudio'             => 'Android Studio',
						'arduino-light'             => 'Arduino Light',
						'arta'                      => 'Arta',
						'ascetic'                   => 'Ascetic',
						'atelier-cave-dark'         => 'Atelier Cave Dark',
						'atelier-cave-light'        => 'Atelier Cave Light',
						'atelier-dune-dark'         => 'Atelier Dune Dark',
						'atelier-dune-light'        => 'Atelier Dune Light',
						'atelier-estuary-dark'      => 'Atelier Estuary Dark',
						'atelier-estuary-light'     => 'Atelier Estuary Light',
						'atelier-forest-dark'       => 'Atelier Forest Dark',
						'atelier-forest-light'      => 'Atelier Forest Light',
						'atelier-heath-dark'        => 'Atelier Heath Dark',
						'atelier-heath-light'       => 'Atelier Heath Light',
						'atelier-lakeside-dark'     => 'Atelier Lakeside Dark',
						'atelier-lakeside-light'    => 'Atelier Lakeside Light',
						'atelier-plateau-dark'      => 'Atelier Plateau Dark',
						'atelier-plateau-light'     => 'Atelier Plateau Light',
						'atelier-savanna-dark'      => 'Atelier Savanna Dark',
						'atelier-savanna-light'     => 'Atelier Savanna Light',
						'atelier-seaside-dark'      => 'Atelier Seaside Dark',
						'atelier-seaside-light'     => 'Atelier Seaside Light',
						'atelier-sulphurpool-dark'  => 'Atelier Sulphurpool Dark',
						'atelier-sulphurpool-light' => 'Atelier Sulphurpool Light',
						'atom-one-dark-reasonable'  => 'Atom One Dark Reasonable',
						'atom-one-dark'             => 'Atom One Dark',
						'atom-one-light'            => 'Atom One Light',
						'brown-paper'               => 'Brown Paper',
						'codepen-embed'             => 'Codepen Embed',
						'color-brewer'              => 'Color Brewer',
						'darcula'                   => 'Darcula',
						'dark'                      => 'Dark',
						'darkula'                   => 'Darkula',
						'docco'                     => 'Docco',
						'dracula'                   => 'Dracula',
						'far'                       => 'Far',
						'foundation'                => 'Foundation',
						'github-gist'               => 'Github Gist',
						'github'                    => 'Github',
						'gml'                       => 'Gml',
						'googlecode'                => 'Google Code',
						'grayscale'                 => 'Grayscale',
						'gruvbox-dark'              => 'Gruvbox Dark',
						'gruvbox-light'             => 'Gruvbox Light',
						'hopscotch'                 => 'Hopscotch',
						'hybrid'                    => 'Hybrid',
						'idea'                      => 'Idea',
						'ir-black'                  => 'Ir Black',
						'isbl-editor-dark'          => 'Isbl Editor Dark',
						'isbl-editor-light'         => 'Isbl Editor Light',
						'kimbie.dark'               => 'Kimbie Dark',
						'kimbie.light'              => 'Kimbie Light',
						'lightfair'                 => 'Lightfair',
						'magula'                    => 'Magula',
						'mono-blue'                 => 'Mono Blue',
						'monokai-sublime'           => 'Monokai Sublime',
						'monokai'                   => 'Monokai',
						'nord'                      => 'Nord',
						'obsidian'                  => 'Obsidian',
						'ocean'                     => 'Ocean',
						'paraiso-dark'              => 'Paraiso Dark',
						'paraiso-light'             => 'Paraiso Light',
						'pojoaque'                  => 'Pojoaque',
						'purebasic'                 => 'Purebasic',
						'qtcreator_dark'            => 'Qtcreator Dark',
						'qtcreator_light'           => 'Qtcreator Light',
						'railscasts'                => 'Railscasts',
						'rainbow'                   => 'Rainbow',
						'routeros'                  => 'Routeros',
						'school-book'               => 'School Book',
						'shades-of-purple'          => 'Shades Of Purple',
						'solarized-dark'            => 'Solarized Dark',
						'solarized-light'           => 'Solarized Light',
						'sunburst'                  => 'Sunburst',
						'tomorrow-night-blue'       => 'Tomorrow Night Blue',
						'tomorrow-night-bright'     => 'Tomorrow Night Bright',
						'tomorrow-night-eighties'   => 'Tomorrow Night Eighties',
						'tomorrow-night'            => 'Tomorrow Night',
						'tomorrow'                  => 'Tomorrow',
						'vs'                        => 'VS',
						'vs2015'                    => 'VS 2015',
						'xcode'                     => 'Xcode',
						'xt256'                     => 'Xt 256',
						'zenburn'                   => 'Zenburn',
					),
				),

				array(
					'name'    => 'highlight_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_highlight',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
					)
				),

				array(
					'label'   => __( 'Example', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/highlight-js' ),
					'type'    => 'html',
					'parent'  => 'support_highlight',
				),

				// -------------------------------------------------------------------//

				array(
					'section_title' => true,
					'location_id'   => 'clipboard',
					'label'         => __( 'Copy to Clipboard', 'wp-githuber-md' ),
					'desc'          => __( 'clipboard.js', 'wp-githuber-md' ),
				),

				array(
					'name'    => 'support_clipboard',
					'desc'    => __( 'Display a `Copy` button on the highlighting code block. Copy the text into clipboard by clicking the button.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'default'     => 'no',
					'location_id' => 'clipboard',
				),

				array(
					'name'    => 'clipboard_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_clipboard',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
					)
				),

				array(
					'section_title' => true,
					'location_id'   => 'image-paste',
					'label'         => __( 'Image Paste', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_image_paste',
					//'label'     => __( 'Image Paste', 'wp-githuber-md' ),
					'desc'        => githuber_load_view( 'setting/image-paste' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'image-paste',
					'default'     => 'no'
				),

				array(
					'name'    => 'image_paste_src',
					'label'   => __( 'Storage Space', 'wp-githuber-md' ),
					'desc'    => __( 'Images are stored in WordPress\'s <strong>uploads</strong> folder by default. However, you can use Imgur instead of the default place.', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_image_paste',
					'options' => array(
						'default' => __( 'default', 'wp-githuber-md' ),
						'imgur'   => __( 'imgur.com', 'wp-githuber-md' ),
						'smms'    => __( 'sm.ms', 'wp-githuber-md' ),
					)
				),

				array(
                    'name'              => 'imgur_client_id',
					'label'             => __( 'Imgur Client ID', 'wp-githuber-md' ),
					'desc'              => githuber_load_view( 'setting/image-paste-imgur' ),
                    'placeholder'       => '',
                    'type'              => 'text',
					'default'           => '',
					'parent'            => 'support_image_paste',
                    'sanitize_callback' => 'sanitize_text_field',
				),

				array(
                    'name'              => 'smms_api_key',
					'label'             => __( 'sm.ms API Key', 'wp-githuber-md' ),
					'desc'              => githuber_load_view( 'setting/image-paste-smms' ),
                    'placeholder'       => '',
                    'type'              => 'text',
					'default'           => '',
					'parent'            => 'support_image_paste',
                    'sanitize_callback' => 'sanitize_text_field',
				),

				array(
                    'name'    => 'is_image_paste_media_library',
					'label'   => __( 'Upload to Media Library?', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'setting/image-paste-media-library' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
					'parent'  => 'support_image_paste',
				),

				array(
					'section_title' => true,
					'location_id'   => 'table-of-content',
					'label'         => __( 'Table of Content', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_toc',
					//'label'     => __( 'Image Paste', 'wp-githuber-md' ),
					'desc'        => __( 'Support Table of Content.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'table-of-content',
					'default'     => 'no'
				),

				array(
                    'name'    => 'is_toc_widget',
					'label'   => __( 'Widget', 'wp-githuber-md' ),
					'desc'    => __( 'Display a TOC in the widget area for single post.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
					'parent'  => 'support_toc',
				),

				array(
                    'name'    => 'display_toc_in_post',
					'label'   => __( 'Inside a Post', 'wp-githuber-md' ),
					'desc'    => __( 'Insert a TOC inside a post header location.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
					'parent'  => 'support_toc',
				),

				array(
					'name'    => 'post_toc_float',
					'label'   => __( 'Float', 'wp-githuber-md' ),
					'desc'    => __( 'Would you like to float the TOC in the post to left or right?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_toc',
					'options' => array(
						'default' => __( 'default', 'wp-githuber-md' ),
						'right'   => __( 'right', 'wp-githuber-md' ),
						'left'    => __( 'left', 'wp-githuber-md' ),
					)
				),

				array(
                    'name'    => 'post_toc_border',
					'label'   => __( 'Border', 'wp-githuber-md' ),
					'desc'    => __( 'Would you like to show the border of the TOC in the post?', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'yes',
					'parent'  => 'support_toc',
				),

				array(
					'section_title' => true,
					'location_id'   => 'katex',
					'label'         => __( 'KaTex', 'wp-githuber-md' ),
					'desc'          => __( 'KaTex.js', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_katex',
					//'label'     => __( 'KaTeX', 'wp-githuber-md' ),
					'desc'        => __( 'Support <a href="https://terryl.in/en/githuber-md-katax/" target="_blank">KaTeX</a> math typesetting.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'default'     => 'no',
					'location_id' => 'katex',
				),

				array(
					'name'    => 'katex_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_katex',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
						'custom'     => __( 'custom', 'wp-githuber-md' )
					)
				),

				array(
					'label'   => __( 'Example', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/katex' ),
					'type'    => 'html',
					'parent'  => 'support_katex',
				),

				array(
					'section_title' => true,
					'location_id'   => 'mermaid',
					'label'         => __( 'Mermaid', 'wp-githuber-md' ),
					'desc'          => __( 'mermaid.js', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_mermaid',
					//'label'     => __( 'Mermaid', 'wp-githuber-md' ),
					'desc'        => __( 'Support <a href="https://terryl.in/en/githuber-md-mermaid/" target="_blank">Mermaid.js</a>, a Markdownish Syntax for Generating Charts.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'location_id' => 'mermaid',
					'has_child'   => true,
					'default'     => 'no'
				),

				array(
					'name'    => 'mermaid_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_mermaid',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
					)
				),

				array(
					'label'   => __( 'Example', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/mermaid' ),
					'type'    => 'html',
					'parent'  => 'support_mermaid',
				),

				array(
					'section_title' => true,
					'location_id'   => 'flowchart',
					'label'         => __( 'Flow Chart', 'wp-githuber-md' ),
					'desc'          => __( 'flowchart.js', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_flowchart',
					//'label'     => __( 'Flow Chart', 'wp-githuber-md' ),
					'desc'        => __( 'Support <a href="https://terryl.in/en/githuber-md-flow-chart/" target="_blank">flowchart.js</a> to draws simple SVG flow chart diagrams.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'flowchart',
					'default'     => 'no',
				),

				array(
					'name'    => 'flowchart_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_flowchart',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
					)
				),

				array(
					'label'   => __( 'Example', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/flowchart' ),
					'type'    => 'html',
					'parent'  => 'support_flowchart',
				),

				array(
					'section_title' => true,
					'location_id'   => 'sequence-diagram',
					'label'         => __( 'Sequence Diagrams', 'wp-githuber-md' ),
					'desc'          => __( 'sequence-diagrams.js', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_sequence_diagram',
					//'label'     => __( 'Sequence Diagrams', 'wp-githuber-md' ),
					'desc'        => __( 'Support <a href="https://terryl.in/en/githuber-md-sequence-diagrams/" target="_blank">js-sequence-diagrams</a> to turn text into vector UML sequence diagrams.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'sequence-diagram',
					'default'     => 'no',
				),

				array(
					'name'    => 'sequence_diagram_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_sequence_diagram',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
					)
				),

				array(
					'label'   => __( 'Example', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/sequence' ),
					'type'    => 'html',
					'parent'  => 'support_sequence_diagram',
				),

				array(
					'section_title' => true,
					'location_id'   => 'mathjax',
					'label'         => __( 'MathJax', 'wp-githuber-md' ),
					'desc'          => __( 'MathJax.js', 'wp-githuber-md' ),
				),
			
				array(
					'name'        => 'support_mathjax',
					'desc'        => __( 'MathJax displays mathematical notation in web browsers, using LaTeX markup. ', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'mathjax',
					'default'     => 'no',
				),
	
				array(
					'name'    => 'mathjax_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'cloudflare',
					'parent'  => 'support_mathjax',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
					)
				),
	
				array(
					'label'   => __( 'Example', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/mathjax' ),
					'type'    => 'html',
					'parent'  => 'support_mathjax',
				),

				array(
					'section_title' => true,
					'location_id'   => 'support-emojify',
					'label'         => __( 'Emojify', 'wp-githuber-md' ),
					'desc'          => __( 'emojify.js', 'wp-githuber-md' ),
				),

				array(
					'name'        => 'support_emojify',
					'desc'        => __( 'Display emojis on Markdown editor preview pane and frontend posts.', 'wp-githuber-md' ),
					'type'        => 'toggle',
					'has_child'   => true,
					'location_id' => 'support-emoji',
					'default'     => 'no',
				),

				array(
					'name'    => 'emojify_src',
					'label'   => __( 'File Host', 'wp-githuber-md' ),
					'desc'    => __( 'Use this library with a CDN service or self-hosted (default)?', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => 'default',
					'parent'  => 'support_emojify',
					'options' => array(
						'default'    => __( 'default', 'wp-githuber-md' ),
						'cloudflare' => 'cdnjs.cloudflare.com',
						'jsdelivr'   => 'cdn.jsdelivr.net',
					)
				),

				array(
					'name'    => 'emojify_emoji_size',
					'label'   => __( 'Image Size', 'wp-githuber-md' ),
					'desc'    => __( 'What size would you want the emojis to be in your posts.', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => '1.5em',
					'parent'  => 'support_emojify',
					'options' => array(
						'1em' => '1.00 (em)',
						'1.125em' => '1.125 (em)',
						'1.25em' => '1.250 (em)',
						'1.375em' => '1.375 (em)',
						'1.5em' => '1.500 (em)' . ' - ' . __( 'default', 'wp-githuber-md' ),
						'1.625em' => '1.625 (em)',
						'1.75em' => '1.750 (em)',
					)
				),
			),

			'githuber_extensions' => array(

				array(
					'name'    => 'support_mardown_extra',
					'label'   => __( 'Markdown Extra', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'setting/markdown-extra' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no'
				),
				array(
					'name'    => 'support_task_list',
					'label'   => __( 'GFM Task List', 'wp-githuber-md' ),
					'desc'    => __( 'Support Github Flavored Markdown task lists.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no'
				),

				array(
					'desc' => githuber_load_view( 'example/gfm-task-list' ),
					'type' => 'html',
				),

				array(
					'label'         => __( 'Githuber MD Extensions', 'wp-githuber-md' ),
					'section_title' => true,
				),

				array(
					'name'    => 'support_html_figure',
					'label'   => __( 'HTML5 Figure', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/html5-figure' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no'
				),

				array(
					'name'    => 'support_inline_code_keyboard_style',
					'label'   => __( 'Inline Code Block with Keyboard Style', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'example/inline-code-keyboard-style' ),
					'type'    => 'toggle',
					'size'    => 'sm',
					'default' => 'no'
				),
			),

			'githuber_preferences' => array(
				array(
					'name'    => 'post_link_target_attribute',
					'label'   => __( 'Link Opening Method', 'wp-githuber-md' ),
					'desc'    => __( 'For links in posts, please specify where to open the linked document.', 'wp-githuber-md' ),
					'type'    => 'radio',
					'default' => '_self',
					'options' => array(
						'_self'  => __( 'Same window. (default)', 'wp-githuber-md' ),
						'_blank' => __( 'New window.', 'wp-githuber-md' ),
					)
				),

				array(
					'name'    => 'allow_shortcode',
					'label'   => __( 'Shortcode', 'wp-githuber-md' ),
					'desc'    => __( 'Allow using shortcode in Markdown text.', 'wp-githuber-md' ) . '<br />' . __( 'Please understand that shortcode is processed to HTML by PHP, not JavaScript, therefore live-preview panel does not support it.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'default' => 'yes',
				),

				array(
					'name'    => 'smart_quotes',
					'label'   => __( 'Smart Quotes', 'wp-githuber-md' ),
					'desc'    => githuber_load_view( 'setting/smart-quotes' ),
					'type'    => 'toggle',
					'default' => 'yes',
				),

				array(
					'name'    => 'restore_ampersands',
					'label'   => __( 'Ampersands in URL', 'wp-githuber-md' ),
					'desc'    => __( 'Replace `&amp;amp;` to `&` in URLs.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'default' => 'no',
				),

				array(
					'name'    => 'support_wpseo_analysis',
					'label'   => __( 'Yoast SEO Analysis', 'wp-githuber-md' ),
					'desc'    => __( "Support Yoast SEO readability analysis.", 'wp-githuber-md' ),
					'type'    => 'toggle',
					'default' => 'no',
				),

				array(
					'name'    => 'clear_all_settings',
					'label'   => __( 'Clear all Settings', 'wp-githuber-md' ),
					'desc'    => __( 'Clear all settings when uninstalling WP GitHuber MD.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'default' => 'no',
				),

				array(
					'name'    => 'decode_code_blocks',
					'label'   => __( 'Decode Code Blocks', 'wp-githuber-md' ),
					'desc'    => sprintf( __( 'If you have met issues similar to issue #30, <a href="%s" target="_blank">#89</a>, try enabling this.', 'wp-githuber-md' ), 'https://github.com/terrylinooo/githuber-md/issues/89' ),
					'type'    => 'toggle',
					'default' => 'no',
				),

				array(
					'name'    => 'richeditor_by_default',
					'label'   => __( 'Default: Rich Editor', 'wp-githuber-md' ),
					'desc'    => __( 'Notice: Your users might be confused because that if they swith to Markdown editor and then switch back to Rich editor or Gutenberg, the Markdown text of that single post will be lost. That is because that Rich editor and Markdown editor use differnt fields to store data.', 'wp-githuber-md' ),
					'type'    => 'toggle',
					'default' => 'no',
				),
			),

			'githuber_about' => array(

				array(
					'name'  => 'plugin_about_author',
					'label' => __( 'Author', 'wp-githuber-md' ),
					'desc'  => 'Terry L. @ Taiwan.',
					'type'  => 'html'
				),

				array(
					'name'  => 'plugin_about_version',
					'label' => __( 'Version', 'wp-githuber-md' ),
					'desc'  => GITHUBER_PLUGIN_VERSION,
					'type'  => 'html'
				),

				array(
					'name'  => 'plugin_about_github',
					'label' => __( 'GitHub Repository', 'wp-githuber-md' ),
					'desc'  => githuber_load_view( 'setting/about-github-repo' ),
					'type'  => 'html'
				),

				array(
					'name'  => 'plugin_theme',
					'label' => __( 'Theme', 'wp-githuber-md' ),
					'desc'  => githuber_load_view( 'setting/theme-description' ),
					'type'  => 'html'
				),
			),
		);
	}

	/**
	 * Register the plugin page.
	 */
	public function setting_admin_menu() {
		switch ( $this->menu_position ) {
			case 'menu':
			case 'plugins':
			case 'options':
			default:
				$menu_function = 'add_' . $this->menu_position . '_page';
				$menu_function(
					__( 'WP Githuber MD ', 'wp-githuber-md' ),
					__( 'WP Githuber MD', 'wp-githuber-md' ),
					'manage_options',
					$this->menu_slug,
					array( $this, 'setting_plugin_page' )
				);
				break;
		}
	}

	/**
	* Display the plugin settings options page.
	*/
	public function setting_plugin_page() {

		$git_url_plugin = 'https://github.com/terrylinooo/githuber-md';

		echo '<div class="githuber-md-info-bar">';
		echo '	<div class="logo-info"><img src="' . GITHUBER_PLUGIN_URL . '/assets/images/logo.png" class="githuber-md-logo"></div>';
		echo '	<div class="version-info">';
		echo '    <a href="' . $git_url_plugin . '/issues" target="_blank">' . __( 'Report an issue', 'wp-githuber-md' ) . '</a>  ';
		echo '    ' . __( 'Version', 'wp-githuber-md' ) . ': <a href="' . $git_url_plugin . '" target="_blank">' . GITHUBER_PLUGIN_VERSION . '</a>  ';
		echo '  </div>';
		echo '</div>';
		echo '<div class="wrap">';

		settings_errors();

		self::$setting_api->show_navigation();
		self::$setting_api->show_forms();

		echo '<div>' . __( 'Maintain social distancing. Wash your hands frequently.', 'wp-githuber-md' ) . '</div>';
		echo '<div>' . __( 'Stay at home. Write your articles with Githuber MD.', 'wp-githuber-md' ) . '</div>';

		echo '</div>';
	}

	/**
	 * Filters the action links displayed for each plugin in the Network Admin Plugins list table.
	 *
	 * @param  array  $links Original links.
	 * @param  string $file  File position.
	 * @return array Combined links.
	 */
	public function plugin_action_links( $links, $file ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return $links;
		}

		if ( $file == $this->githuber_plugin_name ) {
			$links[] = '<a href="' . admin_url( "options-general.php?page=" . $this->menu_slug ) . '">' . __( 'Settings', 'wp-githuber-md' ) . '</a>';
			return $links;
		}
	}

	/**
	 * Add links to plugin meta information on plugin list page.
	 *
	 * @param  array  $links Original links.
	 * @param  string $file  File position.
	 * @return array Combined links.
	 */
	public function plugin_extend_links( $links, $file ) {
		if ( ! current_user_can( 'install_plugins' ) ) {
			return $links;
		}

		if ( $file == $this->githuber_plugin_name ) {
			$links[] = '<a href="https://github.com/terrylinooo/githuber-md" target="_blank">' . __( 'View GitHub project', 'wp-githuber-md' ) . '</a>';
			$links[] = '<a href="https://github.com/terrylinooo/githuber-md/issues" target="_blank">' . __( 'Report issues', 'wp-githuber-md' ) . '</a>';
		}
		return $links;
	}
}
Controllers/SpellCheck.php000066600000003027151747671010011617 0ustar00<?php
/**
 * Class SpellCheck
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.10.2
 * @version 1.10.2
 * 
 * Hunspell dictories source:
 * https://spellcheck-dictionaries.github.io/
 * 
 */

namespace Githuber\Controller;

class SpellCheck extends ControllerAbstract {

	/**
	 * We use a JavaScript library that is called `codemirror-spell-checker`, and this is its version number.
	 *
	 * @link https://github.com/sparksuite/codemirror-spell-checker
	 *
	 * @var string
	 */
	public $spellcheck_varsion = '1.1.2';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'admin_init' ) );
    }
    
	/**
	 * Initalize to WP `admin_init` hook.
	 */
	public function admin_init() {
        add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
    }

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {

	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {
        wp_enqueue_script( 'githuber-md-typo-check', $this->githuber_plugin_url . 'assets/vendor/editor.md/lib/codemirror/addon/spellcheck/typo.js', array( 'editormd' ), $this->spellcheck_varsion, true );
        wp_enqueue_script( 'githuber-md-spell-check', $this->githuber_plugin_url . 'assets/vendor/editor.md/lib/codemirror/addon/spellcheck/spell-checker.js', array( 'editormd' ), $this->spellcheck_varsion, true );
	}
}Controllers/FetchRemoteImage.php000066600000011607151747671010012755 0ustar00<?php
/**
 * Class HtmlToMarkdown
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.3.0
 * @version 1.4.2
 */

namespace Githuber\Controller;

class FetchRemoteImage extends ControllerAbstract {

	/**
	 * The remote image URL list.
	 *
	 * @var array
	 */
	public static $image_list = array();

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'admin_init' ) );
	}

	/**
	 * Initalize to WP `admin_init` hook.
	 */
	public function admin_init() {
		$user          = wp_get_current_user();
		$allowed_roles = array( 'editor', 'administrator', 'author' );

		// For security reasons, only authorized logged-in users can update content.
		if ( array_intersect( $allowed_roles, $user->roles ) || is_super_admin() ) {

			// Add the sidebar metabox to posts.
			add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
		}
	}

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {

	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {

	}

	/**
	 * Register the `HtmlToMarkdown` meta box in the post-editor.
	 */
	public function add_meta_box() {
		
		if ( ! githuber_current_user_can( 'edit_posts' ) ) {
			return false;
		}

		add_meta_box(
			'remote_image_meta_box',
			__( 'Fetch Remote Image', 'wp-githuber-md' ) . '<div class="bg-icon-md"></div>',
			array( $this, 'show_meta_box' ),
			null,
			'side',
			'high'
		);
	}
	
	/**
	 * Show `HtmlToMarkdown` meta box.
	 */
	public function show_meta_box() {
		echo githuber_load_view( 'metabox/fetch-remote-image' );
	}

    /**
     * Find all remote image URLs, fetch them and save them into local folder.
     * 
     * @param string $post_content Post content
     * @return string
     */
    public static function covert( $post_content ) {

        preg_match_all( '/<img.*?src=[\'"](.*?)[\'"].*?>/i', $post_content, $matches );

        $img_elements = $matches[1];
        $site_url     = str_replace( array( 'https', 'http' ), '', get_site_url() );

        foreach( $img_elements as $i => $img_remote_url ) {

            if ( strpos( $img_remote_url, $site_url ) !== false ) {
				// Yep, the two images are in the same domain name.
				// Nothing to do.
            } else {
		
				$grabbed_image_content = self::grab_image( $img_remote_url );

				if ( ! empty( $grabbed_image_content ) ) {
					$new_url = self::save_image( $grabbed_image_content, $img_remote_url );
				}

                self::$image_list[ $i ]['before'] = $img_remote_url;
                self::$image_list[ $i ]['after']  = $new_url;
			}
		}
		
		// Replace the remote image URLs with the new local images.
		foreach ( self::$image_list as $image_info ) {
			$post_content = str_replace( $image_info['before'], $image_info['after'], $post_content );
		}

		return $post_content;
    }

    /**
     * Grab remote image.
     *
     * @param string $url    The remote target.
     * @return bool
     */
    public static function grab_image( $url ) {

        $ch = curl_init( $url );

        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13' );
    
		$raw = curl_exec($ch);
    
        $http_response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
        curl_close ($ch);
    
        if ( 200 === $http_response_code ) {
            return $raw;
        } else {
            // The remote site probably blocks this connection.
            return '';
        }
	}
	
	/**
	 * Save images into local folder.
	 *
	 * @param string $content The image content.
	 * @param string $url     The remote image's URL.
	 * @return void
	 */
	public static function save_image( $content,  $url )
	{
		$image_info = getimagesize( $url );

		$post_id = githuber_get_current_post_id();

		if ( empty( $post_id ) ) {
			return '';
		}

		$ext = '';
		if ( ! empty( $image_info['mime'] )) {
			if ( 'image/png' === $image_info['mime'] ) {
				$ext = 'png';
			} elseif ( 'image/gif' === $image_info['mime'] ) {
				$ext = 'gif';
			} elseif ( 'image/jepg' === $image_info['mime'] ) {
				$ext = 'jpg';
			}
		} else {
			return '';
		}

		$upload_dir  = wp_upload_dir();
		$upload_path = $upload_dir['path'];
		$online_path = $upload_dir['url'];

		$filename = 'post-' . $post_id . '-' . uniqid() . '.' . $ext;

		file_put_contents( $upload_path . '/' . $filename, $content );

		if ( is_ssl() ) {
			$online_path = str_replace( 'http://', 'https://', $online_path );
		}

		return $online_path . '/' . $filename;
	}
}Controllers/ControllerAbstract.php000066600000003150151747671010013406 0ustar00<?php

/**
 * Class ControllerAbstract
 * 
 * Controllers are specifically used for admin (backend) use.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.0.0
 */

namespace Githuber\Controller;

abstract class ControllerAbstract {

	/**
	 * Version.
	 *
	 * @var string
	 */
	public $version;

	/**
	 * Text domain for transation.
	 *
	 * @var string
	 */
	public $text_domain;

	/**
	 * The plugin url.
	 *
	 * @var string
	 */
	public $githuber_plugin_url;

	/**
	 * The plugin directory.
	 *
	 * @var string
	 */
	public $githuber_plugin_dir;

	/**
	 * The plugin loader's path.
	 *
	 * @var string
	 */
	public $githuber_plugin_path;

	/**
	 * Plugin's name.
	 *
	 * @var string
	 */
	public $githuber_plugin_name;

	/**
	 * Constructer.
	 * 
	 * @return void
	 */
	public function __construct() {
		/**
		 * Basic plugin information. Mapping from the Constant in the plugin loader script.
		 */
		$this->githuber_plugin_name = GITHUBER_PLUGIN_NAME;
		$this->githuber_plugin_url  = GITHUBER_PLUGIN_URL;
		$this->githuber_plugin_dir  = GITHUBER_PLUGIN_DIR;
		$this->githuber_plugin_path = GITHUBER_PLUGIN_PATH;
		$this->version              = GITHUBER_PLUGIN_VERSION;
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	abstract public function init();
	
	/**
	 * Register CSS style files.
	 * 
	 * @param string Hook suffix string.
	 * @return void
	 */
	abstract public function admin_enqueue_styles( $hook_suffix );

	/**
	 * Register JS files.
	 * 
	 * @param string Hook suffix string.
	 * @return void
	 */
	abstract public function admin_enqueue_scripts( $hook_suffix );
}
Controllers/Register.php000066600000003312151747671010011363 0ustar00<?php
/**
 * Class Register
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.7.0
 */

namespace Githuber\Controller;

class Register extends ControllerAbstract {

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 */
	public function init() {

		add_action( 'admin_init', array( $this, 'admin_init' ) );

		if ( 'yes' === githuber_get_option( 'disable_revision', 'githuber_markdown' ) ) {
			add_action( 'admin_init', array( $this , 'remove_revisions' ), 999 );
		}

		if ( 'yes' === githuber_get_option( 'disable_autosave', 'githuber_markdown' ) ) {
			add_action( 'wp_print_scripts', array( $this , 'remove_autosave' ), 10 );
		}
	}

	/**
	 * Initalize to WP `admin_init` hook.
	 */
	function admin_init() {
		global $current_user;

		if ( user_can_richedit() ) {
			update_user_option( $current_user->ID, 'rich_editing', 'false', true );
		}
		add_filter( 'user_can_richedit' , '__return_false', 50 );

		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
	}

	/**
	 * Remove revisions.
	 */
	public function remove_revisions() {
		foreach ( get_post_types() as $post_type ) {
			remove_post_type_support( $post_type, 'revisions' );
		}
	}

	/**
	 * Remove auto-save function.
	 */
	function remove_autosave() {
		wp_deregister_script('autosave');
	}

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {
		wp_enqueue_style( 'custom_wp_admin_css', $this->githuber_plugin_url . 'assets/css/admin-style.css', array(), $this->version, 'all' );
	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {

	}

			
}
Controllers/Monolog.php000066600000002733151747671010011217 0ustar00<?php
/**
 * Class Monolog
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.6.0
 * @version 1.6.0
 */

namespace Githuber\Controller;

class Monolog {

	public static $instance;

	public static function get_instance() {

		if ( ! isset( self::$instance ) ) {

			$settings = array(
				'name'  => 'githuber-md',
				'path'  => GITHUBER_PLUGIN_DIR . 'logs/markdown.log',
				'level' => \Monolog\Logger::DEBUG,
			);

			self::$instance = new \Monolog\Logger( $settings['name'] );
			self::$instance->pushHandler( new \Monolog\Handler\StreamHandler( $settings['path'],  $settings['level'] ) );

		}
		return self::$instance;
	}

	/**
	 * Record Markdown processing logs for debug propose.
	 *
	 * @param string $message
	 * @param array  $data
	 *
	 * @return void
	 */
	public static function logger( $message, $addon_data = array() ) {

		if ( GITHUBER_DEBUG_MODE ) {
			$trace = debug_backtrace();

			$caller_class  = $trace[1]['class'];
			$caller_method = $trace[1]['function'];
	
			$caller_class = str_replace('Githuber\\', '', $caller_class);
			$caller_class = str_replace('\\', '/', $caller_class);
	
			$caller_info = array(
				'class'  => $caller_class,
				'method' => $caller_method,
				//'track'  => end( $track_file ) . '(' . __LINE__ . ')',
			);
	
			$info_data['caller'] = $caller_info;
	
			if ( ! empty( $addon_data ) ) {
				$info_data['info'] = $addon_data;
			}

			self::get_instance()->info( $message . "\n", $info_data );	
		}
	}
}
Controllers/KeywordSuggestion.php000066600000011156151747671010013300 0ustar00<?php
/**
 * Class KeywordSuggestion
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.15.0
 * @version 1.15.0
 */

namespace Githuber\Controller;

class KeywordSuggestion extends ControllerAbstract {

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'admin_init' ) );
	}

	/**
	 * Initalize to WP `admin_init` hook.
	 */
	public function admin_init() {
		$user          = wp_get_current_user();
		$allowed_roles = array( 'editor', 'administrator', 'author' );

		// For security reasons, only authorized logged-in users can update content.
		if ( array_intersect( $allowed_roles, $user->roles ) || is_super_admin() ) {
			add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
            add_action( 'wp_ajax_githuber_keyword_suggestion', array( $this, 'admin_githuber_keyword_suggestion' ) );

			// Add the sidebar metabox to posts.
			add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
		}
	}

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {

	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {
		wp_enqueue_script( 'githuber-md-ks', $this->githuber_plugin_url . 'assets/js/githuber-md-ks.js', array(), $this->version, true );

		$data['ajax_url'] = admin_url( 'admin-ajax.php' );
		$data['post_id']  = githuber_get_current_post_id();

		wp_localize_script( 'githuber-md-ks', 'ks_config', $data );
	}

	/**
	 * Register the `HtmlToMarkdown` meta box in the post-editor.
	 */
	public function add_meta_box() {
		
		if ( ! githuber_current_user_can( 'edit_posts' ) ) {
			return false;
		}

		add_meta_box(
			'keyword_suggesion_meta_box',
			__( 'Keyword Suggestions', 'wp-githuber-md' ) . '<div class="bg-icon-md"></div>',
			array( $this, 'show_meta_box' ),
			null,
			'side',
			'high'
		);
	}
	
	/**
	 * Show `HtmlToMarkdown` meta box.
	 */
	public function show_meta_box() {
		echo githuber_load_view( 'metabox/keyword-suggestion-tool' );
	}

	/**
	 * Do action hook for keyword suggestions.
	 */
	public function admin_githuber_keyword_suggestion() {

        if ( 
            isset( $_GET['_wpnonce'], $_GET['post_id'], $_GET['keyword'] ) && 
            wp_verify_nonce( $_GET['_wpnonce'], 'keyword_suggession_action' ) && 
            current_user_can( 'edit_post', $_GET['post_id'] )
        ) {

            // Default response.
            $response = array(
                'success' => false,
                'result'  => '',
            );

            if ( ! empty( $_GET['keyword'] ) ) {
                $keyword = $_GET['keyword'];
            }

            $lang = str_replace( '_', '-', get_locale() );

            $keyword_string = $this->query( $keyword, $lang );
            $keyword_list   = explode(',', $keyword_string);
            $result         = '';
    
            foreach ($keyword_list as $word) {
                $result .= '<span class="githuber-md-keyword">' . $word . '</span>';
            }

            if ( ! empty( $result ) ) {
                $response = array(
                    'success' => true,
                    'result'  => $result,
                );
            }
        
        } else {
            $response['error'] = __( 'Error while uploading file.', 'wp-githuber-md' );
        }

        header('Content-type: application/json');

		echo json_encode( $response );

		// To avoid wp_ajax return "0" string to break the vaild json string.
		wp_die();
    }
    
    /**
     * Query a keyword's long-tail terms through Google Suggestions.
     *
     * @param string $keyword A keyword that you want to
     * 
     * @return string
     */
    private function query( $keyword = '', $lang = 'en-US' ) {

        if ( ! empty( $keyword ) && strlen( $keyword ) > 4 ) {
    
            $url = 'http://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl=' . $lang . '&q=' . urlencode( $keyword );
            $ch  = curl_init( $url );

            curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'GET' );
            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
            curl_setopt( $ch, CURLOPT_TIMEOUT, 5 );
            curl_setopt( $ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0' );

            $data = curl_exec( $ch );

            curl_close($ch);

            if ( null !== ( $data = json_decode( $data, true ) ) ) {
                $keywords = $data[1];

                return implode( ',', $keywords );
            }
        }

        return '';
    }
}
Controllers/ImagePaste.php000066600000017674151747671010011636 0ustar00<?php
/**
 * Class ImagePaste
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.1
 * @version 1.12.2
 */

namespace Githuber\Controller;

class ImagePaste extends ControllerAbstract {

	/**
	 * The version of inline-attachment.js we are using.
	 *
	 * @var string
	 */
	public $imagepaste_version = '2.0.3';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'admin_init' ) );
	}

	/**
	 * Initalize to WP `admin_init` hook.
	 */
	public function admin_init() {
		$user          = wp_get_current_user();
		$allowed_roles = array( 'editor', 'administrator', 'author' );

		// For security reasons, only authorized logged-in users can upload images.
		if ( array_intersect( $allowed_roles, $user->roles ) || is_super_admin() ) {
			add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
			add_action( 'wp_ajax_githuber_image_paste', array( $this, 'admin_githuber_image_paste' ) );
		}
	}

	/**
	 * Register CSS style files.
	 */
	public function admin_enqueue_styles( $hook_suffix ) {

	}

	/**
	 * Register JS files.
	 */
	public function admin_enqueue_scripts( $hook_suffix ) {
		wp_enqueue_script( 'image-paste', $this->githuber_plugin_url . 'assets/vendor/inline-attachment/inline-attachment.min.js', array(), $this->imagepaste_version, true );
		wp_enqueue_script( 'image-paste-codemirror', $this->githuber_plugin_url . 'assets/vendor/inline-attachment/codemirror-4.inline-attachment.min.js', array(), $this->imagepaste_version, true );
	}

	/**
	 * Do action hook for image paste.
	 */
	public function admin_githuber_image_paste() {
		$response = array();
		
		if ( isset( $_FILES['file'], $_GET['_wpnonce'], $_GET['post_id'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'image_paste_action_' . $_GET['post_id'] ) && current_user_can( 'edit_post', $_GET['post_id'] ) ) {
			$image_src        = githuber_get_option( 'image_paste_src', 'githuber_modules' );
			$imgur_client_id  = githuber_get_option( 'imgur_client_id', 'githuber_modules' );
			$smms_api_key     = githuber_get_option( 'smms_api_key', 'githuber_modules' );
			$is_media_library = githuber_get_option( 'is_image_paste_media_library', 'githuber_modules' );

			$file = $_FILES['file'];

			$is_file_image = getimagesize( $file['tmp_name'] ) ? true : false;
			$file_mimetype = $file['type'];

			if ( ! $is_file_image || 'image/png' !== $file_mimetype ) {
				$response['error'] = sprintf( __( 'Error while processing your request to %s!', 'wp-githuber-md' ), 'Githuber MD' );
				echo json_encode( $response );
				wp_die();
			}
	
			if ( 'imgur' === $image_src && ! empty( $imgur_client_id ) ) {
				
				if ( function_exists( 'curl_init') ) {
					$image = file_get_contents( $file['tmp_name'] );
					$data  = $this->upload_to_imgur( $image, $imgur_client_id );

					if ( true === $data['success'] ) {
						$response['filename'] = $data['data']['link'];
					} else {
						$response['error'] = sprintf( __( 'Error while processing your request to %s!', 'wp-githuber-md' ), 'Imgur' );
					}
				} else {
					$response['error'] = __( 'PHP Curl is not installed on your system.', 'wp-githuber-md' );
				}
			} elseif ( 'smms' === $image_src ) {
				
				if ( function_exists( 'curl_init') ) {
					$image    = $file['tmp_name'];
					$filename = uniqid() . '.png';

					$data  = $this->upload_to_smms( $image, $filename, $smms_api_key );

					if ( 'success' === $data['code'] ) {
						$response['filename'] = $data['data']['url'];
					} else {
						$response['error'] = sprintf( __( 'Error while processing your request to %s!', 'wp-githuber-md' ), 'sm.mse' ) . '(' . json_encode($data) . ')';

						if ( ! empty( $data['msg'] ) ) {
							$response['error'] .= $data['msg'];
						}
					}
				} else {
					$response['error'] = __( 'PHP Curl is not installed on your system.', 'wp-githuber-md' );
				}
			} else {

				if ( 'no' === $is_media_library ) {
					$upload_dir  = wp_upload_dir();
					$upload_path = $upload_dir['path'];
					$online_path = $upload_dir['url'];

					$filename = uniqid() . '.' . ( pathinfo( $file['name'], PATHINFO_EXTENSION ) ? : 'png' );

					if ( is_ssl() ) {
						$online_path = str_replace( 'http://', 'https://', $online_path );
					}

					move_uploaded_file( $file['tmp_name'], $upload_path . '/' . $filename );
					$response['filename'] = $online_path . '/' . $filename;

				} else {

					$attachment_id = media_handle_upload( 'file', $_GET['post_id'] );
					$online_path   = wp_get_attachment_url( $attachment_id );

					if ( is_ssl() ) {
						$online_path = str_replace( 'http://', 'https://', $online_path );
					}
					$response['filename'] = $online_path;
				}
			}
		} else {
			$response['error'] = __( 'Error while uploading file.', 'wp-githuber-md' );
		}
		echo json_encode( $response );

		// To avoid wp_ajax return "0" string to break the vaild json string.
		wp_die();
	}

	/**
	 * Upload images to Imgur.com
	 * 
	 * @param string $image     Image binary string.
	 * @param string $client_id Imgur application Client ID.
	 * @return array Response from Imgur image API.
	 */
	public function upload_to_imgur( $image, $client_id ) {
		$header_data = array( "Authorization: Client-ID $client_id" );
		$post_data   = array( 'image' => base64_encode( $image ) );

		$ch = curl_init();

		curl_setopt( $ch, CURLOPT_URL, 'https://api.imgur.com/3/image.json' );
		curl_setopt( $ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)' );
		curl_setopt( $ch, CURLOPT_POST, 1 );
		curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_data );
		curl_setopt( $ch, CURLOPT_HTTPHEADER, $header_data );
		curl_setopt( $ch, CURLOPT_TIMEOUT, 30 );
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
		
		$result = curl_exec( $ch );
		curl_close( $ch );

		return json_decode( $result, true );
	}

	/**
	 * Upload images to sm.ms (v2 API)
	 * 
	 * @param string $image     Image binary string.
	 * @param string $filename  Filename.
	 * @param string $api_key   sm.ms API key (required since v2 API.)
	 * @return array Response from sm.ms image API.
	 */
	public function upload_to_smms( $image, $filename, $api_key ) {
		$image     = curl_file_create( $image, 'image/png', $filename );
		$post_data = array( 'smfile' => $image );

		$ch = curl_init();

		curl_setopt( $ch, CURLOPT_URL, 'https://sm.ms/api/v2/upload' );
		curl_setopt( $ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)' );
		curl_setopt( $ch, CURLOPT_POST, 1 );
		curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_data );
		curl_setopt( $ch, CURLOPT_TIMEOUT, 30 );
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );

		$header[] = 'Content-Type: multipart/form-data';
    	$header[] = 'Authorization: ' . $api_key;

    	curl_setopt( $ch, CURLOPT_HTTPHEADER, $header );
		
		$result = curl_exec( $ch );
		curl_close( $ch );

		return json_decode( $result, true );
	}

	/**
	 * Upload images to sm.ms (deprecated)
	 * 
	 * @param string $image     Image binary string.
	 * @param string $filename  Filename.
	 * @return array Response from sm.ms image API.
	 */
	public function upload_to_smms_v1( $image, $filename ) {
		$image     = curl_file_create( $image, 'image/png', $filename );
		$post_data = array( 'smfile' => $image );

		$ch = curl_init();

		curl_setopt( $ch, CURLOPT_URL, 'https://sm.ms/api/upload' );
		curl_setopt( $ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)' );
		curl_setopt( $ch, CURLOPT_POST, 1 );
		curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_data );
		curl_setopt( $ch, CURLOPT_TIMEOUT, 30 );
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
		
		$result = curl_exec( $ch );
		curl_close( $ch );

		return json_decode( $result, true );
	}
}
autoload.php000066600000003006151747671010007101 0ustar00<?php

/**
 * Githuber Class autoloader.
 *
 * @package   Githuber
 * @author    Terry Lin <terrylinooo>
 * @license   GPLv3 (or later)
 * @link      https://terryl.in
 * @copyright 2018 Terry Lin
 */

/**
 * Class autoloader
 */
spl_autoload_register( function( $class_name ) {

	$include_path = '';

	$class_name = ltrim( $class_name, '\\' );

	$wp_utils_mapping = array(         
		'Githuber'              => '../Githuber',
		'Githuber_Settings_API' => 'class-settings-api',
		'Githuber_Widget_Toc'    => 'class-widget-toc',
	);

	if ( array_key_exists( $class_name, $wp_utils_mapping ) ) {

		$include_path = GITHUBER_PLUGIN_DIR . 'src/wp_utilities/' . $wp_utils_mapping[ $class_name ] . '.php';

	} else {
		
		if ( false !== strpos( $class_name, '\\' ) ) {
			if ( false === strpos( $class_name, 'Githuber' ) ) {
				return false;
			}

			$class_name = str_replace('Controller\\', 'Controllers\\', $class_name);
			$class_name = str_replace('Model\\', 'Models\\', $class_name);
			$class_name = str_replace('Module\\', 'Modules\\', $class_name);

			$last_ns_pos = strrpos( $class_name, '\\' );
			$namespace = substr( $class_name, 0, $last_ns_pos );
			$class_name = substr( $class_name, $last_ns_pos + 1 );
			$filename  = GITHUBER_PLUGIN_DIR . '/src/' . str_replace( '\\', '/', $namespace ) . '/';
			$filename .= str_replace( '_', '/', $class_name ) . '.php';
	
			$include_path = str_replace( 'Githuber/', '', $filename );
		}
	}

	if ( ! empty( $include_path ) && is_readable( $include_path ) ) {
		require $include_path;
	}
});
Modules/MarkdownExtraParser.php000066600000021026151747671010012646 0ustar00<?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;

class MarkdownExtraParser extends ParsedownExtra {

	// Stores shortcodes we remove and then replace
	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 ) {
			$this->InlineTypes['%'] = array( 'Figure' );
			$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.
	 */
	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
	 * @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( '`', '&#x60;', $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( '&#x60;', '`', $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( '&#x60;', '`', $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';
	}
}
Modules/MarkdownParser.php000066600000020611151747671010011641 0ustar00<?php
/**
 * Module Name: MarkdownParser
 * Module Description: Parse Markdown plaintext into HTML plaintext.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.11.2
 */

namespace Githuber\Module;
use Parsedown;

class MarkdownParser extends Parsedown {

	// Stores shortcodes we remove and then replace
	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() {

		$is_html5_figure = githuber_get_option( 'support_html_figure', 'githuber_extensions' );

		if ( 'no' !== $is_html5_figure ) {
			$this->InlineTypes['%'] = array( 'Figure' );
			$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.
	 */
	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
	 * @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' ) ) {
			if ( '}' === substr( $matches[1], -1 ) && '{' !== substr( $matches[1], 0, 1 ) ) {
				return '<code class="kb-btn">' . $this->hash_block( esc_html( $matches[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( '`', '&#x60;', $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( '&#x60;', '`', $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( '&#x60;', '`', $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';
	}
}
Modules/Highlight.php000066600000026352151747671010010621 0ustar00<?php
/**
 * Module Name: HightLight
 * Module Description: A syntax highlighter by highlight.js
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.4.0
 * 
 */

namespace Githuber\Module;

class Highlight extends ModuleAbstract {

	/**
	 * The version of Prism we are using.
	 *
	 * @var string
	 */
	public $highlight_version = '9.15.10';

	/**
	 * The priority order to load CSS file, the value should be higher than theme's.
	 * Overwrite the theme's style to make sure that it's safe to display the correct syntax highlight.
	 *
	 * @var integer
	 */
	public $css_priority = 999;

	// This is what highlight.js uses.
	public static $highlight_codes = array(
		'1c'             => '1C:Enterprise (v7, v8)',
		'abnf'           => 'Augmented Backus-Naur Form',
		'accesslog'      => 'Access log',
		'actionscript'   => 'ActionScript',
		'ada'            => 'Ada',
		'angelscript'    => 'AngelScript',
		'apache'         => 'Apache',
		'applescript'    => 'AppleScript',
		'arcade'         => 'ArcGIS Arcade',
		'arduino'        => 'Arduino',
		'armasm'         => 'ARM Assembly',
		'asciidoc'       => 'AsciiDoc',
		'aspectj'        => 'AspectJ',
		'autohotkey'     => 'AutoHotkey',
		'autoit'         => 'AutoIt',
		'avrasm'         => 'AVR Assembler',
		'awk'            => 'Awk',
		'axapta'         => 'Microsoft Axapta (now Dynamics 365)',
		'bash'           => 'Bash',
		'basic'          => 'Basic',
		'bnf'            => 'Backus–Naur Form',
		'brainfuck'      => 'Brainfuck',
		'c'              => 'C',
		'cal'            => 'C/AL',
		'capnproto'      => 'Cap’n Proto',
		'ceylon'         => 'Ceylon',
		'clean'          => 'Clean',
		'clojure-repl'   => 'Clojure REPL',
		'clojure'        => 'Clojure',
		'cmake'          => 'CMake',
		'coffeescript'   => 'CoffeeScript',
		'coq'            => 'Coq',
		'cos'            => 'Cache Object Script',
		'cpp'            => 'C++',
		'crmsh'          => 'crmsh',
		'crystal'        => 'Crystal',
		'cs'             => 'C#',
		'csp'            => 'CSP',
		'css'            => 'CSS',
		'd'              => 'D',
		'dart'           => 'Dart',
		'delphi'         => 'Delphi',
		'diff'           => 'Diff',
		'django'         => 'Django',
		'dns'            => 'DNS Zone file',
		'dockerfile'     => 'Dockerfile',
		'dos'            => 'DOS .bat',
		'dsconfig'       => 'dsconfig',
		'dts'            => 'Device Tree',
		'dust'           => 'Dust',
		'ebnf'           => 'Extended Backus-Naur Form',
		'elixir'         => 'Elixir',
		'elm'            => 'Elm',
		'erb'            => 'ERB (Embedded Ruby)',
		'erlang-repl'    => 'Erlang REPL',
		'erlang'         => 'Erlang',
		'excel'          => 'Excel',
		'fix'            => 'FIX',
		'flix'           => 'Flix',
		'fortran'        => 'Fortran',
		'fsharp'         => 'F#',
		'gams'           => 'GAMS',
		'gauss'          => 'GAUSS',
		'gcode'          => 'G-code (ISO 6983)',
		'gherkin'        => 'Gherkin',
		'glsl'           => 'GLSL',
		'gml'            => 'GML',
		'go'             => 'Golang',
		'golo'           => 'Golo',
		'gradle'         => 'Gradle',
		'groovy'         => 'Groovy',
		'haml'           => 'Haml',
		'handlebars'     => 'Handlebars',
		'haskell'        => 'Haskell',
		'haxe'           => 'Haxe',
		'hsp'            => 'HSP',
		'htmlbars'       => 'HTMLBars',
		'http'           => 'HTTP (Header Plaintext)',
		'hy'             => 'Hy',
		'inform7'        => 'Inform 7',
		'ini'            => 'TOML, also INI',
		'irpf90'         => 'IRPF90',
		'isbl'           => 'ISBL',
		'java'           => 'Java',
		'javascript'     => 'JavaScript',
		'jboss-cli'      => 'jboss-cli',
		'json'           => 'JSON / JSON with Comments',
		'julia-repl'     => 'Julia REPL',
		'julia'          => 'Julia',
		'kotlin'         => 'Kotlin',
		'lasso'          => 'Lasso',
		'ldif'           => 'LDIF',
		'leaf'           => 'Leaf',
		'less'           => 'Less',
		'lisp'           => 'Lisp',
		'livecodeserver' => 'LiveCode',
		'livescript'     => 'LiveScript',
		'llvm'           => 'LLVM IR',
		'lsl'            => 'LSL (Linden Scripting Language)',
		'lua'            => 'Lua',
		'makefile'       => 'Makefile',
		'markdown'       => 'Markdown',
		'mathematica'    => 'Mathematica',
		'matlab'         => 'Matlab',
		'maxima'         => 'Maxima',
		'mel'            => 'MEL',
		'mercury'        => 'Mercury',
		'mipsasm'        => 'MIPS Assembly',
		'mizar'          => 'Mizar',
		'mojolicious'    => 'Mojolicious',
		'monkey'         => 'Monkey',
		'moonscript'     => 'MoonScript',
		'n1ql'           => 'N1QL',
		'nginx'          => 'Nginx',
		'nimrod'         => 'Nim (formerly Nimrod)',
		'nix'            => 'Nix',
		'nsis'           => 'NSIS',
		'objectivec'     => 'Objective-C',
		'ocaml'          => 'OCaml',
		'openscad'       => 'OpenSCAD',
		'oxygene'        => 'Oxygene',
		'parser3'        => 'Parser3',
		'perl'           => 'Perl',
		'pf'             => 'pf.conf',
		'pgsql'          => 'PostgreSQL SQL dialect and PL/pgSQL',
		'php'            => 'PHP',
		'plaintext'      => 'Plaintext',
		'pony'           => 'Pony',
		'powershell'     => 'PowerShell',
		'processing'     => 'Processing',
		'profile'        => 'Python profile',
		'prolog'         => 'Prolog',
		'properties'     => 'Properties',
		'protobuf'       => 'Protocol Buffers',
		'puppet'         => 'Puppet',
		'purebasic'      => 'PureBASIC',
		'python'         => 'Pythin',
		'q'              => 'Q',
		'qml'            => 'QML',
		'r'              => 'R',
		'reasonml'       => 'ReasonML',
		'rib'            => 'RenderMan RIB',
		'roboconf'       => 'Roboconf',
		'routeros'       => 'Microtik RouterOS script',
		'rsl'            => 'RenderMan RSL',
		'ruby'           => 'Ruby',
		'ruleslanguage'  => 'Oracle Rules Language',
		'rust'           => 'Rust',
		'sas'            => 'SAS',
		'scala'          => 'Scala',
		'scheme'         => 'Scheme',
		'scilab'         => 'Scilab',
		'scss'           => 'SCSS',
		'shell'          => 'Shell Session',
		'smali'          => 'Smali',
		'smalltalk'      => 'Smalltalk',
		'sml'            => 'SML (Standard ML)',
		'sqf'            => 'SQF',
		'sql'            => 'SQL (Structured Query Language)',
		'stan'           => 'Stan',
		'stata'          => 'Stata',
		'step21'         => 'STEP Part 21',
		'stylus'         => 'Stylus',
		'subunit'        => 'SubUnit',
		'swift'          => 'Swift',
		'taggerscript'   => 'Tagger Script',
		'tap'            => 'Test Anything Protocol',
		'tcl'            => 'Tcl',
		'tex'            => 'TeX',
		'thrift'         => 'Thrift',
		'tp'             => 'TP',
		'twig'           => 'Twig',
		'typescript'     => 'TypeScript',
		'vala'           => 'Vala',
		'vbnet'          => 'VB.NET',
		'vbscript-html'  => 'VBScript in HTML',
		'vbscript'       => 'VBScript in HTML',
		'verilog'        => 'Verilog',
		'vhdl'           => 'VHDL',
		'vim'            => 'Vim Script',
		'x86asm'         => 'Intel x86 Assembly',
		'xl'             => 'XL',
		'xml'            => 'HTML, XML',
		'xquery'         => 'XQuery',
		'yaml'           => 'YAML',
		'zephir'         => 'Zephir',
	);

	/**
	 * Constant. Should be same as `Markdown::MD_POST_META_HIGHLIGHT`.
	 */
	const MD_POST_META_HIGHLIGHT = '_githuber_highlightjs';
	
	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_styles' ), $this->css_priority );
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_HIGHLIGHT ) ) {
			$highlight_src   = githuber_get_option( 'highlight_src', 'githuber_modules' );
			$highlight_theme = githuber_get_option( 'highlight_theme', 'githuber_modules' );

			$theme = ( 'default' === $highlight_theme || empty( $highlight_theme ) ) ? 'default' : $highlight_theme;

			switch ( $highlight_src ) {
				case 'cloudflare':
					$style_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/' . $this->highlight_version . '/styles/' . $theme . '.min.css';
					break;

				default:
					$style_url[] = $this->githuber_plugin_url . 'assets/vendor/highlight.js/styles/' . $theme . '.min.css';
					break;
			}

			foreach ( $style_url as $key => $url ) {
				wp_enqueue_style( 'highlight-css-' . $key, $url, array(), $this->highlight_version, 'all' );
			}
		}
	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_HIGHLIGHT ) ) {
			$highlight_src         = githuber_get_option( 'highlight_src', 'githuber_modules' );
			$post_id               = githuber_get_current_post_id();
			$highlight_meta_string = get_metadata( 'post', $post_id, self::MD_POST_META_HIGHLIGHT );
			$highlight_meta_array  = explode( ',', $highlight_meta_string[0] );

			switch ( $highlight_src ) {
				case 'cloudflare':
					$script_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/' . $this->highlight_version . '/highlight.min.js';

					if ( ! empty( $highlight_meta_array ) ) {
						foreach ( array_reverse( $highlight_meta_array ) as $component_name ) {
							if ( 'c' === $component_name ) {
								$component_name = 'cpp';
							}
							$script_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/' . $this->highlight_version . '/languages/' . $component_name . '.min.js';
						}
					}
					break;

				default: 
					$script_url[] = $this->githuber_plugin_url . 'assets/vendor/highlight.js/highlight.min.js';

					if ( ! empty( $highlight_meta_array ) ) {
						foreach ( array_reverse( $highlight_meta_array ) as $component_name ) {
							if ( 'c' === $component_name ) {
								$component_name = 'cpp';
							}
							$script_url[] = $this->githuber_plugin_url . 'assets/vendor/highlight.js/languages/' . $component_name . '.min.js';
						}
					}

					break;
			}

			foreach ( $script_url as $key => $url ) {
				wp_enqueue_script( 'highlight-js-' . $key, $url, array(), $this->highlight_version, true );
			}
		}
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '
			<script id="module-highlight-js">
				(function($) {
					$(function() {
						$("pre code").each(function(i, e) {
							var thisclass = $(this).attr("class");

							if (typeof thisclass !== "undefined") {
								if (
									thisclass.indexOf("katex") === -1 &&
									thisclass.indexOf("mermaid") === -1 &&
									thisclass.indexOf("seq") === -1 &&
									thisclass.indexOf("flow") === -1
								) {
									if (typeof hljs !== "undefined") {
										$(this).closest("pre").addClass("hljs");
										hljs.highlightBlock(e);
									} else {
										
										console.log("%c WP Githuber MD %c You have enabled highlight.js modules already, but you have to update this post to take effect, identifying which file should be loaded.\nGithuber MD does not load a whole-fat-packed file for every post.", "background: #222; color: #bada55", "color: #637338");
									}
								}
							}
						});
					});
				})(jQuery);
			</script>
		';
		echo preg_replace( '/\s+/', ' ', $script );
	}
}
Modules/KaTeX.php000066600000011256151747671010007663 0ustar00<?php
/**
 * Module Name: KaTex
 * Module Description: Use KaTex markup for complex equations and other geekery.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.14.0
 */

namespace Githuber\Module;

class KaTeX extends ModuleAbstract {

	/**
	 * The version of KaTeX we are using.
	 *
	 * @var string
	 */
	public $katex_version = '0.12.0';

	/**
	 * The priority order to load CSS file, the value should be higher than theme's.
	 * Overwrite the theme's style it's safe to display the correct syntax highlight.
	 *
	 * @var integer
	 */
	public $css_priority = 1000;

	/**
	 * Constants.
	 */
	const MD_POST_META_KATEX = '_is_githuber_katex';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_styles'), $this->css_priority );
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_KATEX ) ) {
			
			$option = githuber_get_option( 'katex_src', 'githuber_modules' );

			switch ( $option ) {
				case 'cloudflare':
					$style_url = 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/' . $this->katex_version . '/katex.min.css';
					break;

				case 'jsdelivr':
					$style_url = 'https://cdn.jsdelivr.net/npm/katex@' . $this->katex_version . '/dist/katex.min.css';
					break;

				default:
					$style_url = $this->githuber_plugin_url . 'assets/vendor/katex/katex.min.css';
					break;
			} 
			wp_enqueue_style( 'katex', $style_url, array(), $this->katex_version, 'all' );
		}
	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_KATEX ) ) {

			$option = githuber_get_option( 'katex_src', 'githuber_modules' );

			switch ( $option ) {
				case 'cloudflare':
					$script_url = 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/' . $this->katex_version . '/katex.min.js';
					break;

				case 'jsdelivr':
					$script_url = 'https://cdn.jsdelivr.net/npm/katex@' . $this->katex_version . '/dist/katex.min.js';
					break;

				default:
					$script_url = $this->githuber_plugin_url . 'assets/vendor/katex/katex.min.js';
					break;
			} 
			wp_enqueue_script( 'katex', $script_url, array(), $this->katex_version, true );
		}
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '
			<script id="module-katex">
				(function($) {
					$(function() {
						if (typeof katex !== "undefined") {
                            if ($(".language-katex").length > 0) {
								$(".language-katex").parent("pre").attr("style", "text-align: center; background: none;");
								$(".language-katex").addClass("katex-container").removeClass("language-katex");
								$(".katex-container").each(function() {
									var katexText = $(this).text();
									var el = $(this).get(0);
									if ($(this).parent("code").length == 0) {
										try {
											katex.render(katexText, el)
										} catch (err) {
											$(this).html("<span class=\'err\'>" + err)
										}
									}
								});
							}
							if ($(".katex-inline").length > 0) {
								$(".katex-inline").each(function() {
									var katexText = $(this).text();
									var el = $(this).get(0);
									if ($(this).parent("code").length == 0) {
										try {
											katex.render(katexText, el)
										} catch (err) {
											$(this).html("<span class=\'err\'>" + err)
										}
									}
								});
							}
						}
					});
                })(jQuery);
			</script>
		';
		echo preg_replace( '/\s+/', ' ', $script );
	}

	/**
	 * Katex Inline Markup
	 * 
	 * Ex.
	 * `$$ x_{1,2} = {-b\pm\sqrt{b^2 - 4ac} \over 2a}.$$`
	 *
	 * @param string  $content HTML or Markdown content.
	 * @return void
	 */
	public static function katex_inline_markup( $content ) {

		$regex = '%<code>\$\$((?:[^$]+ |(?<=(?<!\\\\)\\\\)\$ )+)(?<!\\\\)\$\$<\/code>%ix';
		$content = preg_replace_callback( $regex, function() {
			$matches = func_get_arg(0);

			if ( ! empty( $matches[1] ) ) {
				$katex = $matches[1];
				$katex = str_replace( array( '&lt;', '&gt;', '&quot;', '&#039;', '&#038;', '&amp;', "\n", "\r" ), array( '<', '>', '"', "'", '&', '&', ' ', ' ' ), $katex );
				return '<code class="katex-inline">' . trim( $katex ) . '</code>';
			}
		}, $content );

		return $content;
	}
}
Modules/ModuleAbstract.php000066600000003146151747671010011617 0ustar00<?php

/**
 * Class ModuleAbstract
 * 
 * Modules are specifically used for frontend.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.0.0
 */

namespace Githuber\Module;

abstract class ModuleAbstract {

	/**
	 * The plugin url.
	 *
	 * @var string
	 */
	public $githuber_plugin_url;

	/**
	 * Post Id.
	 *
	 * @var integer
	 */
	public static $front_post_id = 0;

	/**
	 * Constructer.
	 * 
	 * @return void
	 */
	public function __construct() {
		/**
		 * Basic plugin information. Mapping from the Constant in the plugin loader script.
		 */
		$this->githuber_plugin_url  = GITHUBER_PLUGIN_URL;
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	abstract public function init();
	
	/**
	 * Register CSS style files for frontend use.
	 *
	 * @return void
	 */
	abstract public function front_enqueue_styles();

	/**
	 * Register JS files for frontend use.
	 *
	 * @return void
	 */
	abstract public function front_enqueue_scripts();

	/**
	 * Print Javascript plaintext in page footer.
	 *
	 * @return void
	 */
	abstract public function front_print_footer_scripts();

	/**
	 * Check if this module should be loaded.
	 */
	public function is_module_should_be_loaded( $meta_name ) {
		if ( empty( self::$front_post_id ) ) {
			// Get current post ID if an user is viewing a post.
			self::$front_post_id = githuber_get_current_post_id();
		} 
		
		if ( ! empty( self::$front_post_id ) ) {
			$post_meta = get_metadata( 'post', self::$front_post_id, $meta_name );
			if ( empty( $post_meta[0] ) ) {
				return false;
			}
			return (bool) $post_meta[0];
		}
		return false;
	}
}
Modules/Mermaid.php000066600000004667151747671010010275 0ustar00<?php
/**
 * Module Name: Mermaid
 * Module Description: Generation of diagrams and flowcharts from text in a similar manner as markdown.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.4.0
 * @version 1.4.0
 */

namespace Githuber\Module;

class Mermaid extends ModuleAbstract {

	/**
	 * The version of flowchart.js we are using.
	 *
	 * @var string
	 */
    public $mermaid_version = '8.9.0';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Constants.
	 */
	const MD_POST_META_MERMAID = '_is_githuber_mermaid';

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {

	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_MERMAID ) ) {
			$option = githuber_get_option( 'mermaid_src', 'githuber_modules' );

			switch ( $option ) {
                case 'cloudflare':
                    $script_url = 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/' . $this->mermaid_version . '/mermaid.min.js';
                    break;

				case 'jsdelivr':
                    $script_url = 'https://cdn.jsdelivr.net/npm/mermaid@' . $this->mermaid_version . '/dist/mermaid.min.js';
					break;

				default:
					$script_url = $this->githuber_plugin_url . 'assets/vendor/mermaid/mermaid.min.js';
					break;
			} 

			wp_enqueue_script( 'mermaid', $script_url, array(), $this->mermaid_version, true );
		}
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '
			<script id="module-mermaid">
				(function($) {
					$(function() {
						if (typeof mermaid !== "undefined") {
                            if ($(".language-mermaid").length > 0) {
								$(".language-mermaid").parent("pre").attr("style", "text-align: center; background: none;");
								$(".language-mermaid").addClass("mermaid").removeClass("language-mermaid");
								mermaid.init();
                            }
						}
					});
                })(jQuery);
			</script>
		';
		echo preg_replace( '/\s+/', ' ', $script );
	}
}
Modules/SequenceDiagram.php000066600000007457151747671010011754 0ustar00<?php
/**
 * Module Name: Sequence Diagram
 * Module Description: Turn text into vector UML sequence diagrams.
 * 
 * JavaScript package: https://github.com/bramp/js-sequence-diagrams
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.0.0
 */

namespace Githuber\Module;

class SequenceDiagram extends ModuleAbstract {

	/**
	 * The version of js-sequence-diagrams.js we are using.
	 *
	 * @var string
	 */
    public $sequence_diagram_version = '1.0.6';

	/**
	 * The version of raphael.js we are using.
	 *
	 * @var string
	 */
	public $raphael_version = '2.2.27';
	
	/**
	 * The version of underscore.js we are using.
	 *
	 * @var string
	 */
    public $underscore_version = '2.2.27';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Constants.
	 */
	const MD_POST_META_SEQUENCE = '_is_githuber_sequence';

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {

	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_SEQUENCE ) ) {
			$option = githuber_get_option( 'flowchart_src', 'githuber_modules' );

			switch ( $option ) {
				case 'cloudflare':
					$script_url[0] = 'https://cdnjs.cloudflare.com/ajax/libs/raphael/' . $this->raphael_version . '/raphael.min.js';
					$script_url[1] = 'https://cdnjs.cloudflare.com/ajax/libs/underscore.js/' . $this->underscore_version . '/underscore-min.js';
					$script_url[2] = 'https://cdnjs.cloudflare.com/ajax/libs/js-sequence-diagrams/' . $this->sequence_diagram_version . '/js-sequence-diagram.min.js';
					break;

				case 'jsdelivr':
					$script_url[0] = 'https://cdn.jsdelivr.net/npm/raphael@' . $this->raphael_version . '/raphael.min.js';
					$script_url[1] = 'https://cdn.jsdelivr.net/npm/underscore@' . $this->underscore_version . '/underscore.min.js';
					$script_url[2] = 'https://cdn.jsdelivr.net/gh/bramp/js-sequence-diagrams@v' . $this->sequence_diagram_version . '/build/sequence-diagram-min.js';
					break;

				default:
					$script_url[0] = $this->githuber_plugin_url . 'assets/vendor/raphael/raphael.min.js';
					$script_url[1] = $this->githuber_plugin_url . 'assets/vendor/underscore/underscore.min.js';
					$script_url[2] = $this->githuber_plugin_url . 'assets/vendor/js-sequence-diagrams/sequence-diagram.min.js';
					break;
			} 

			wp_enqueue_script( 'raphael', $script_url[0], array(), $this->raphael_version, true );
			wp_enqueue_script( 'underscore', $script_url[1], array(), $this->underscore_version, true );
			wp_enqueue_script( 'sequence-diagrams', $script_url[2], array(), $this->sequence_diagram_version, true );
		}
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '
			<script id="module-sequence-diagram">
				(function($) {
					$(function() {
						if (typeof $.fn.sequenceDiagram !== "undefined") {
							$(".language-sequence").parent("pre").attr("style", "text-align: center; background: none;");
							$(".language-seq").parent("pre").attr("style", "text-align: center; background: none;");
							$(".language-sequence").addClass("sequence-diagram").removeClass("language-sequence");
							$(".language-seq").addClass("sequence-diagram").removeClass("language-seq");
							$(".sequence-diagram").sequenceDiagram({
								theme: "simple"
							});
						}
					});
                })(jQuery);
			</script>
		';
		echo preg_replace( '/\s+/', ' ', $script );
	}
}
Modules/Clipboard.php000066600000006407151747671010010610 0ustar00<?php
/**
 * Module Name: Clipboard
 * Module Description: Copy text into clipboard.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.9.2
 * @version 1.10.1
 */

namespace Githuber\Module;

class Clipboard extends ModuleAbstract {

	/**
	 * The version of flowchart.js we are using.
	 *
	 * @var string
	 */
    public $clipboard_version = '2.0.4';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
	
        $clipboard_src = githuber_get_option( 'clipboard_src', 'githuber_modules' );

        switch ( $clipboard_src ) {
            case 'cloudflare':
                $script_url = 'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/' . $this->clipboard_version . '/clipboard.min.js';
                break;

            case 'jsdelivr':
                $script_url = 'https://cdn.jsdelivr.net/npm/clipboard@' . $this->clipboard_version . '/dist/clipboard.min.js';
                break;

            default:
                $script_url = $this->githuber_plugin_url . 'assets/vendor/clipboard/clipboard.min.js';
                break;
        } 

		wp_enqueue_script( 'clipboard', $script_url, array(), $this->clipboard_version, true );
	}

	/**
	 * Register CSS style files for frontend use.
	 */
	public function front_enqueue_styles() {

	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '
			<script id="module-clipboard">

				(function($) {

					$(function() {
	
						var pre = document.getElementsByTagName("pre");
						var pasteContent = document.getElementById("paste-content");
						var hasLanguage = false;
		
						for (var i = 0; i < pre.length; i++) {
							var codeClass = pre[i].children[0].className;
							var isLanguage = codeClass.indexOf("language-");
		
							var excludedCodeClassNames = [
								"language-katex",
								"language-seq",
								"language-sequence",
								"language-flow",
								"language-flowchart",
								"language-mermaid",
							];
		
							var isExcluded = excludedCodeClassNames.indexOf(codeClass);
		
							if (isExcluded !== -1) {
								isLanguage = -1;
							}
		
							if (isLanguage !== -1) {
								var button = document.createElement("button");
								button.className = "copy-button";
								button.textContent = "Copy";
			
								pre[i].appendChild(button);
								hasLanguage = true;
							}
						};

						if (hasLanguage) {
							var copyCode = new ClipboardJS(".copy-button", {
								target: function(trigger) {
									return trigger.previousElementSibling;
								}
							});

							copyCode.on("success", function(event) {
								event.clearSelection();
								event.trigger.textContent = "Copied";
								window.setTimeout(function() {
									event.trigger.textContent = "Copy";
								}, 2000);
							});

						}
					});

				})(jQuery);

			</script>
		';

		echo preg_replace( '/\s+/', ' ', $script );
	}
}
Modules/Toc.php000066600000005701151747671010007432 0ustar00<?php
/**
 * Module Name: Table of Content
 * Module Description: Display table of content in article section.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.9.0
 * @version 1.10.1
 */

namespace Githuber\Module;

class Toc extends ModuleAbstract {

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	public function init() {

		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );

		if ( 'yes' === githuber_get_option( 'display_toc_in_post', 'githuber_modules' ) ) {

			add_filter( 'the_content', function( $string ) {

				// Only single page will display TOC.
				if ( ! is_single() ) {
					return $string;
				}
	
				$css = githuber_get_option( 'post_toc_float', 'githuber_modules' );

				if ( 'yes' === githuber_get_option( 'post_toc_border', 'githuber_modules' ) ) {
					$css .= ' with-border';
				}

				return '<div class="post-toc-block float-' . $css . '"> 
					<div class="post-toc-header">' . __( 'Table of Content', 'wp-githuber-md' ) . '</div>
					<nav id="md-post-toc" class="md-post-toc"></nav>
					</div>' . $string;
			}, 10, 1 );
		}
	}

	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {

	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
	
		// Only single page will display TOC.
		if ( ! is_single() ) {
			return;
		}

		wp_register_script( 'githuber-toc', GITHUBER_PLUGIN_URL . 'assets/js/jquery.toc.min.js', array( 'jquery' ), '1.0.1' );
		wp_enqueue_script( 'githuber-toc' );
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {

		// Only single page will display TOC.
		if ( ! is_single() ) {
			return;
		}

		$script = '
			<script id="module-toc">
				(function($) {
					$(function() {
		';

		// Show TOC in post.
		if ( 'yes' == githuber_get_option( 'display_toc_in_post', 'githuber_modules' ) ) {

			$script .= '
				$("#md-post-toc").initTOC({
					selector: "h2, h3, h4, h5, h6",
					scope: ".post",
				});

				$("#md-post-toc a").click(function(e) {
					e.preventDefault();
					var aid = $( this ).attr( "href" );
					$( "html, body" ).animate( { scrollTop: $(aid).offset().top - 80 }, "slow" );
				});
			';
		}

		// Show TOC in widget area.
		if ( 'yes' == githuber_get_option( 'is_toc_widget', 'githuber_modules' ) ) {

			$script .= '
				$("#md-widget-toc").initTOC({
					selector: "h2, h3, h4, h5, h6",
					scope: ".post",
				});

				$("#md-widget-toc a").click(function(e) {
					e.preventDefault();
					var aid = $( this ).attr( "href" );
					$( "html, body" ).animate( { scrollTop: $(aid).offset().top - 80 }, "slow" );
				});
			';
		}

		$script .= '
					});
				})(jQuery);
			</script>
		';

		echo preg_replace( '/\s+/', ' ', $script );
	}
}
Modules/Prism.php000066600000037107151747671010010004 0ustar00<?php
/**
 * Module Name: Prism
 * Module Description: A syntax highlighter by Prism.js
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.4.0
 * 
 */

namespace Githuber\Module;

class Prism extends ModuleAbstract {

	/**
	 * The version of Prism we are using.
	 *
	 * @var string
	 */
	public $prism_version = '1.15.0';

	/**
	 * The priority order to load CSS file, the value should be higher than theme's.
	 * Overwrite the theme's style to make sure that it's safe to display the correct syntax highlight.
	 *
	 * @var integer
	 */
	public $css_priority = 999;

	// This is what Prism.js uses.
	public static $prism_codes = array(
		'html'              => 'HTML',
		'xml'               => 'XML',
		'svg'               => 'SVG',
		'mathml'            => 'MathML',
		'css'               => 'CSS',
		'clike'             => 'C-like',
		'javascript'        => 'JavaScript',
		'abap'              => 'ABAP',
		'actionscript'      => 'ActionScript',
		'ada'               => 'Ada',
		'apacheconf'        => 'Apache Configuration',
		'apl'               => 'APL',
		'applescript'       => 'AppleScript',
		'arduino'           => 'Arduino',
		'arff'              => 'ARFF',
		'asciidoc'          => 'AsciiDoc',
		'asm6502'           => '6502 Assembly',
		'aspnet'            => 'ASP.NET (C#)',
		'autohotkey'        => 'AutoHotkey',
		'autoit'            => 'AutoIt',
		'bash'              => 'Bash',
		'basic'             => 'BASIC',
		'batch'             => 'Batch',
		'bison'             => 'Bison',
		'brainfuck'         => 'Brainfuck',
		'bro'               => 'Bro',
		'c'                 => 'C',
		'csharp'            => 'C#',
		'cpp'               => 'C++',
		'coffeescript'      => 'CoffeeScript',
		'clojure'           => 'Clojure',
		'crystal'           => 'Crystal',
		'csp'               => 'Content-Security-Policy',
		'css-extras'        => 'CSS Extras',
		'd'                 => 'D',
		'dart'              => 'Dart',
		'diff'              => 'Diff',
		'django'            => 'Django/Jinja2',
		'docker'            => 'Docker',
		'eiffel'            => 'Eiffel',
		'elixir'            => 'Elixir',
		'elm'               => 'Elm',
		'erb'               => 'ERB',
		'erlang'            => 'Erlang',
		'fsharp'            => 'F#',
		'flow'              => 'Flow',
		'fortran'           => 'Fortran',
		'gedcom'            => 'GEDCOM',
		'gherkin'           => 'Gherkin',
		'git'               => 'Git',
		'glsl'              => 'GLSL',
		'go'                => 'Go',
		'graphql'           => 'GraphQL',
		'groovy'            => 'Groovy',
		'haml'              => 'Haml',
		'handlebars'        => 'Handlebars',
		'haskell'           => 'Haskell',
		'haxe'              => 'Haxe',
		'http'              => 'HTTP',
		'hpkp'              => 'HTTP Public-Key-Pins',
		'hsts'              => 'HTTP Strict-Transport-Security',
		'ichigojam'         => 'IchigoJam',
		'icon'              => 'Icon',
		'inform7'           => 'Inform 7',
		'ini'               => 'Ini',
		'io'                => 'Io',
		'j'                 => 'J',
		'java'              => 'Java',
		'jolie'             => 'Jolie',
		'json'              => 'JSON',
		'julia'             => 'Julia',
		'keyman'            => 'Keyman',
		'kotlin'            => 'Kotlin',
		'latex'             => 'LaTeX',
		'less'              => 'Less',
		'liquid'            => 'Liquid',
		'lisp'              => 'Lisp',
		'livescript'        => 'LiveScript',
		'lolcode'           => 'LOLCODE',
		'lua'               => 'Lua',
		'makefile'          => 'Makefile',
		'markdown'          => 'Markdown',
		'markup-templating' => 'Markup templating',
		'matlab'            => 'MATLAB',
		'mel'               => 'MEL',
		'mizar'             => 'Mizar',
		'monkey'            => 'Monkey',
		'n4js'              => 'N4JS',
		'nasm'              => 'NASM',
		'nginx'             => 'nginx',
		'nim'               => 'Nim',
		'nix'               => 'Nix',
		'nsis'              => 'NSIS',
		'objectivec'        => 'Objective-C',
		'ocaml'             => 'OCaml',
		'opencl'            => 'OpenCL',
		'oz'                => 'Oz',
		'parigp'            => 'PARI/GP',
		'parser'            => 'Parser',
		'pascal'            => 'Pascal',
		'perl'              => 'Perl',
		'php'               => 'PHP',
		'php-extras'        => 'PHP Extras',
		'plsql'             => 'PL/SQL',
		'powershell'        => 'PowerShell',
		'processing'        => 'Processing',
		'prolog'            => 'Prolog',
		'properties'        => '.properties',
		'protobuf'          => 'Protocol Buffers',
		'pug'               => 'Pug',
		'puppet'            => 'Puppet',
		'pure'              => 'Pure',
		'python'            => 'Python',
		'q'                 => 'Q (kdb+ database)',
		'qore'              => 'Qore',
		'r'                 => 'R',
		'jsx'               => 'React JSX',
		'tsx'               => 'React TSX',
		'renpy'             => 'Ren\'py',
		'reason'            => 'Reason',
		'rest'              => 'reST (reStructuredText)',
		'rip'               => 'Rip',
		'roboconf'          => 'Roboconf',
		'ruby'              => 'Ruby',
		'rust'              => 'Rust',
		'sas'               => 'SAS',
		'sass'              => 'Sass (Sass)',
		'scss'              => 'Sass (Scss)',
		'scala'             => 'Scala',
		'scheme'            => 'Scheme',
		'smalltalk'         => 'Smalltalk',
		'smarty'            => 'Smarty',
		'sql'               => 'SQL',
		'soy'               => 'Soy (Closure Template)',
		'stylus'            => 'Stylus',
		'swift'             => 'Swift',
		'tcl'               => 'Tcl',
		'textile'           => 'Textile',
		'twig'              => 'Twig',
		'typescript'        => 'TypeScript',
		'vbnet'             => 'VB.Net',
		'velocity'          => 'Velocity',
		'verilog'           => 'Verilog',
		'vhdl'              => 'VHDL',
		'vim'               => 'vim',
		'visual-basic'      => 'Visual Basic',
		'wasm'              => 'WebAssembly',
		'wiki'              => 'Wiki markup',
		'xeora'             => 'Xeora',
		'xojo'              => 'Xojo (REALbasic)',
		'yaml'              => 'YAML',
	);
	
	// The below codes need a parent componet being loaded before.
	public static $prism_component_parent = array(
		'javascript'        => array( 'clike' ),
		'actionscript'      => array( 'javascript' ),
		'arduino'           => array( 'cpp' ),
		'aspnet'            => array( 'markup' ),
		'bison'             => array( 'c' ),
		'c'                 => array( 'clike' ),
		'csharp'            => array( 'clike' ),
		'cpp'               => array( 'c' ),
		'coffeescript'      => array( 'javascript' ),
		'crystal'           => array( 'ruby' ),
		'css-extras'        => array( 'css' ),
		'd'                 => array( 'clike' ),
		'dart'              => array( 'clike' ),
		'django'            => array( 'markup' ),
		'erb'               => array( 'ruby', 'markup-templating' ),
		'fsharp'            => array( 'clike' ),
		//'flow'            => array( 'javascript' ),
		'glsl'              => array( 'clike' ),
		'go'                => array( 'clike' ),
		'groovy'            => array( 'clike' ),
		'haml'              => array( 'ruby' ),
		'handlebars'        => array( 'markup-templating' ),
		'haxe'              => array( 'clike' ),
		'java'              => array( 'clike' ),
		'jolie'             => array( 'clike' ),
		'kotlin'            => array( 'clike' ),
		'less'              => array( 'css' ),
		'markdown'          => array( 'markup' ),
		'markup-templating' => array( 'markup' ),
		'n4js'              => array( 'javascript' ),
		'nginx'             => array( 'clike' ),
		'objectivec'        => array( 'c' ),
		'opencl'            => array( 'cpp' ),
		'parser'            => array( 'markup' ),
		'php'               => array( 'clike', 'markup-templating' ),
		'php-extras'        => array( 'php' ),
		'plsql'             => array( 'sql' ),
		'processing'        => array( 'clike' ),
		'protobuf'          => array( 'clike' ),
		'pug'               => array( 'javascript' ),
		'qore'              => array( 'clike' ),
		'jsx'               => array( 'markup', 'javascript' ),
		'tsx'               => array( 'jsx', 'typescript'),
		'reason'            => array( 'clike' ),
		'ruby'              => array( 'clike' ),
		'sass'              => array( 'css' ),
		'scss'              => array( 'css' ),
		'scala'             => array( 'java' ),
		'smarty'            => array( 'markup-templating' ),
		'soy'               => array( 'markup-templating' ),
		'swift'             => array( 'clike' ),
		'textile'           => array( 'markup' ),
		'twig'              => array( 'markup' ),
		'typescript'        => array( 'javascript' ),
		'vbnet'             => array( 'basic' ),
		'velocity'          => array( 'markup' ),
		'wiki'              => array( 'markup' ),
		'xeora'             => array( 'markup' )
	);

	/**
	 * Constant. Should be same as `Markdown::MD_POST_META_PRISM`.
	 */
	const MD_POST_META_PRISM = '_githuber_prismjs';
	
	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_styles' ), $this->css_priority );
		add_action( 'wp_print_footer_scripts', array( $this, 'auto_loader_config_scripts' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {

		if ( $this->is_module_should_be_loaded( self::MD_POST_META_PRISM ) ) {
			$prism_src         = githuber_get_option( 'prism_src', 'githuber_modules' );
			$prism_theme       = githuber_get_option( 'prism_theme', 'githuber_modules' );
			$prism_line_number = githuber_get_option( 'prism_line_number', 'githuber_modules' );
			$theme             = ( 'default' === $prism_theme || empty( $prism_theme ) ) ? 'prism' : 'prism-' . $prism_theme;

			switch ( $prism_src ) {
				case 'cloudflare':
					$style_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/themes/' . $theme . '.min.css';
					if ( 'yes' === $prism_line_number ) {
						$style_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/plugins/line-numbers/prism-line-numbers.min.css';
					}
					break;

				case 'jsdelivr':
					$style_url[] = 'https://cdn.jsdelivr.net/npm/prismjs@' . $this->prism_version . '/themes/' . $theme . '.css';
					if ( 'yes' === $prism_line_number ) {
						$style_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/plugins/line-numbers/prism-line-numbers.css';
					}
					break;

				default:
					$style_url[] = $this->githuber_plugin_url . 'assets/vendor/prism/themes/' . $theme . '.min.css';
					if ( 'yes' === $prism_line_number ) {
						$style_url[] = $this->githuber_plugin_url . 'assets/vendor/prism/plugins/line-numbers/prism-line-numbers.css';
					}
					break;
			}

			foreach ( $style_url as $key => $url ) {
				wp_enqueue_style( 'prism-css-' . $key, $url, array(), $this->prism_version, 'all' );
			}
		}
	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_PRISM ) ) {
			$prism_src         = githuber_get_option( 'prism_src', 'githuber_modules' );
			$prism_line_number = githuber_get_option( 'prism_line_number', 'githuber_modules' );
			$post_id           = githuber_get_current_post_id();
			$prism_meta_string = get_metadata( 'post', $post_id, self::MD_POST_META_PRISM );
			$prism_meta_array  = explode( ',', $prism_meta_string[0] );

			switch ( $prism_src ) {
				case 'cloudflare':
					$script_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/components/prism-core.min.js';
					$script_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/prism.min.js';

					if ( 'yes' === $prism_line_number ) {
						$script_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/plugins/line-numbers/prism-line-numbers.min.js';
					}

					// AutoLoader plugin
					$script_url[] = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/plugins/autoloader/prism-autoloader.min.js';

					break;

				case 'jsdelivr':
					$script_url[] = 'https://cdn.jsdelivr.net/npm/prismjs@' . $this->prism_version . '/components/prism-core.min.js';
					$script_url[] = 'https://cdn.jsdelivr.net/npm/prismjs@' . $this->prism_version . '/prism.min.js';

					if ( 'yes' === $prism_line_number ) {
						$script_url[] = 'https://cdn.jsdelivr.net/npm/prismjs@' . $this->prism_version . '/plugins/line-numbers/prism-line-numbers.min.js';
					}

					// AutoLoader plugin (Add since 1.11.4)
					$script_url[] = 'https://cdn.jsdelivr.net/npm/prismjs@' . $this->prism_version . '/plugins/autoloader/prism-autoloader.min.js';

					break;

				default: 
					$script_url[] = $this->githuber_plugin_url . 'assets/vendor/prism/components/prism-core.min.js';
					$script_url[] = $this->githuber_plugin_url . 'assets/vendor/prism/prism.min.js';

					if ( 'yes' === $prism_line_number ) {
						$script_url[] = $this->githuber_plugin_url . 'assets/vendor/prism/plugins/line-numbers/prism-line-numbers.min.js';
					}

					// AutoLoader plugin (Add since 1.11.4)
					$script_url[] = $this->githuber_plugin_url . 'assets/vendor/prism/plugins/autoloader/prism-autoloader.min.js';

					break;
			}

			foreach ( $script_url as $key => $url ) {
				wp_enqueue_script( 'prism-js-' . $key, $url, array(), $this->prism_version, true );
			}
		}
	}

	/**
	 * Configure auto loader path.
	 * 
	 * @since 1.11.4
	 */
	public function auto_loader_config_scripts() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_PRISM ) ) {
			$prism_src = githuber_get_option( 'prism_src', 'githuber_modules' );

			switch ( $prism_src ) {
				case 'cloudflare':
					$script_path = 'https://cdnjs.cloudflare.com/ajax/libs/prism/' . $this->prism_version . '/components/';

					break;

				case 'jsdelivr':
					$script_path = 'https://cdn.jsdelivr.net/npm/prismjs@' . $this->prism_version . '/components/';

					break;

				default: 
					$script_path = $this->githuber_plugin_url . 'assets/vendor/prism/components/';

					break;
			}

			$script = '
				<script id="auto_loader_config_scripts">
					Prism.plugins.autoloader.languages_path = "' . $script_path . '";
				</script>
			';

			echo preg_replace( '/\s+/', ' ', $script );
		}
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$prism_line_number = githuber_get_option( 'prism_line_number', 'githuber_modules' );

		if ( 'yes' === $prism_line_number ) {
			$script = '
				<script id="module-prism-line-number">
					(function($) {
						$(function() {
							$("code").each(function() {
								var parent_div = $(this).parent("pre");
								var pre_css = $(this).attr("class");
								if (typeof pre_css !== "undefined" && -1 !== pre_css.indexOf("language-")) {
									parent_div.addClass("line-numbers");
								}
							});
						});
					})(jQuery);
				</script>
			';
			echo preg_replace( '/\s+/', ' ', $script );
		}
	}

	/**
	 * (Deprecated since 1.11.4) (Use auto-loader instead)
	 * 
	 *
	 * Check if component is already loaded or not.
	 * Those scripts are already included in prism.js, so we do not need to load those scripts again.
	 *
	 * @param string $name Prism component name.
	 *
	 * @return boolean
	 */
	public function is_component_already_loaded( $name ) {
		switch ( $name ) {
			case 'markup':
			case 'xml':
			case 'html':
			case 'mathml':
			case 'svg':
			case 'clike':
			case 'javascript':
			case 'js':
				return true;
				break;
			default:
				return false;
		}
	}
}
Modules/Emojify.php000066600000007220151747671010010305 0ustar00<?php
/**
 * Module Name: Emoji
 * Module Description: Emoji are ideograms and smileys used in electronic messages and web pages.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.14.0
 */

namespace Githuber\Module;

/**
 * Emogify
 */
class Emojify extends ModuleAbstract {

	/**
	 * The version of Emojify we are using.
	 *
	 * @var string
	 */
	public $emojify_version = '1.1.0';

	/**
	 * The priority order to load CSS file, the value should be higher than theme's.
	 *
	 * @var integer
	 */
	public $css_priority = 1000;

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_styles'), $this->css_priority );
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}

	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {

		$option = githuber_get_option( 'emojify_src', 'githuber_modules' );

		switch ( $option ) {
			case 'cloudflare':
				$style_url = 'https://cdnjs.cloudflare.com/ajax/libs/emojify.js/' . $this->emojify_version . '/css/basic/emojify.min.css';
				break;

			case 'jsdelivr':
				$style_url = 'https://cdn.jsdelivr.net/npm/emojify.js@' . $this->emojify_version . '/dist/css/basic/emojify.min.css';
				break;

			default:
				$style_url = $this->githuber_plugin_url . 'assets/vendor/emojify/css/emojify.min.css';
				break;
		}
		wp_enqueue_style( 'emojify', $style_url, array(), $this->emojify_version, 'all' );
	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {

		$option = githuber_get_option( 'emojify_src', 'githuber_modules' );

		switch ( $option ) {
			case 'cloudflare':
				$script_url = 'https://cdnjs.cloudflare.com/ajax/libs//emojify.js/' . $this->emojify_version . '/js/emojify.min.js';
				break;

			case 'jsdelivr':
				$script_url = 'https://cdn.jsdelivr.net/npm/emojify.js@' . $this->emojify_version . '/dist/js/emojify.min.js';
				break;

			default:
				$script_url = $this->githuber_plugin_url . 'assets/vendor/emojify/js/emojify.min.js';
				break;
		} 
		wp_enqueue_script( 'emojify', $script_url, array(), $this->emojify_version, true );
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {

		$option = githuber_get_option( 'emojify_src', 'githuber_modules' );

		switch ( $option ) {
			case 'cloudflare':
				// https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/images/basic/+1.png
				$img_dir = 'https://cdnjs.cloudflare.com/ajax/libs//emojify.js/' . $this->emojify_version . '/images/basic';
				break;

			case 'jsdelivr':
				// https://cdn.jsdelivr.net/npm/emojify.js@1.1.0/dist/images/basic/+1.png
				$img_dir = 'https://cdn.jsdelivr.net/npm/emojify.js@' . $this->emojify_version . '/dist/images/basic';
				break;

			default:
				$img_dir = $this->githuber_plugin_url . 'assets/vendor/emojify/images';
				break;
		} 

		$script = '
			<script id="module-emojify">
				(function($) {
					$(function() {
						if (typeof emojify !== "undefined") {
							emojify.setConfig({
								img_dir: "' . $img_dir . '",
								blacklist: {
									"classes": ["no-emojify"],
									"elements": ["script", "textarea", "pre", "code"]
								}
							});
							emojify.run();
						} else {
							console.log("[wp-githuber-md] emogify is undefined.");
						}
					});
				})(jQuery);
			</script>
		';
		echo $script;
	}
}
Modules/FlowChart.php000066600000006047151747671010010602 0ustar00<?php
/**
 * Module Name: Flow Chart
 * Module Description: Draws simple SVG flow chart diagrams from textual representation of the diagram.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.0.0
 */

namespace Githuber\Module;

class FlowChart extends ModuleAbstract {

	/**
	 * The version of flowchart.js we are using.
	 *
	 * @var string
	 */
    public $flowchart_version = '1.14.1'; // 1.11.3 => 1.14.1

	/**
	 * The version of raphael.js we are using.
	 *
	 * @var string
	 */
	public $raphael_version = '2.2.27';
	
	/**
	 * Constants.
	 */
	const MD_POST_META_FLOW = '_is_githuber_flow_chart';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {

	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {

		if ( $this->is_module_should_be_loaded( self::MD_POST_META_FLOW ) ) {

			$option  = githuber_get_option( 'flowchart_src', 'githuber_modules' );

			switch ( $option ) {
				case 'cloudflare':
					$script_url[0] = 'https://cdnjs.cloudflare.com/ajax/libs/raphael/' . $this->raphael_version . '/raphael.min.js';
					$script_url[1] = 'https://cdnjs.cloudflare.com/ajax/libs/flowchart/' . $this->flowchart_version . '/flowchart.min.js';
					break;

				case 'jsdelivr':
					$script_url[0] = 'https://cdn.jsdelivr.net/npm/raphael@' . $this->raphael_version . '/raphael.min.js';

					// It doesn't have the latest files in `release` folder on jsdelivr, rollback to 1.12.1
					$this->flowchart_version = '1.12.1';
					$script_url[1] = 'https://cdn.jsdelivr.net/npm/flowchart.js@' . $this->flowchart_version . '/release/flowchart.min.js';
					break;

				default:
					$script_url[0] = $this->githuber_plugin_url . 'assets/vendor/raphael/raphael.min.js';
					$script_url[1] = $this->githuber_plugin_url . 'assets/vendor/flowchart/flowchart.min.js';
					break;
			} 
			wp_enqueue_script( 'raphael', $script_url[0], array(), $this->raphael_version, true );
			wp_enqueue_script( 'flowchart', $script_url[1], array(), $this->flowchart_version, true );
		}
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '
			<script id="module-flowchart">
				(function($) {
					$(function() {
						if (typeof $.fn.flowChart !== "undefined") {
							if ($(".language-flow").length > 0) {
								$(".language-flow").parent("pre").attr("style", "text-align: center; background: none;");
								$(".language-flow").addClass("flowchart").removeClass("language-flow");
								$(".flowchart").flowChart();
							}
						}
					});
				})(jQuery);
			</script>
		';
		echo preg_replace( '/\s+/', ' ', $script );
	}
}
Modules/MathJax.php000066600000011776151747671010010252 0ustar00<?php
/**
 * Module Name: MathJax
 * Module Description: Use MathJax markup for complex equations and other geekery.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.7.0
 */

namespace Githuber\Module;

class MathJax extends ModuleAbstract {

	/**
	 * The version of MathJax we are using.
	 *
	 * @var string
	 */
	public $mathjax_version = '2.7.7';

	/**
	 * The priority order to load CSS file, the value should be higher than theme's.
	 * Overwrite the theme's style it's safe to display the correct syntax highlight.
	 *
	 * @var integer
	 */
	public $css_priority = 1000;

	/**
	 * Constants.
	 */
	const MD_POST_META_MATHJAX = '_is_githuber_mathjax';

	/**
	 * Constructer.
	 */
	public function __construct() {
		parent::__construct();
	}

	/**
	 * Initialize.
	 *
	 * @return void
	 */
	public function init() {
		//add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_styles'), $this->css_priority );
		add_action( 'wp_enqueue_scripts', array( $this, 'front_enqueue_scripts' ) );
		add_action( 'wp_print_footer_scripts', array( $this, 'front_print_footer_scripts' ) );
	}
 
	/**
	 * Register CSS style files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_styles() {

	}

	/**
	 * Register JS files for frontend use.
	 * 
	 * @return void
	 */
	public function front_enqueue_scripts() {
		if ( $this->is_module_should_be_loaded( self::MD_POST_META_MATHJAX ) ) {

            $option = githuber_get_option( 'mathjax_src', 'githuber_modules' );

			switch ( $option ) {
                case 'cloudflare':
					$script_url = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/' . $this->mathjax_version . '/MathJax.js';
					break;

                case 'jsdelivr':
					$script_url = 'https://cdn.jsdelivr.net/npm/mathjax@' . $this->mathjax_version . '/MathJax.js';
					break;

				default:
					$script_url = $this->githuber_plugin_url . 'assets/vendor/mathjax/MathJax.js';
					break;
			} 
			wp_enqueue_script( 'mathjax', $script_url, array(), $this->mathjax_version, true );
		}
	}

	/**
	 * Print Javascript plaintext in page footer.
	 */
	public function front_print_footer_scripts() {
		$script = '
            <script id="module-mathjax" >
                (function($) {
                    $(function() {
                        if (typeof MathJax !== "undefined") {
							var c = $(".language-mathjax").length;

                            if (c > 0) {
								$(".language-mathjax").each(function(i) {
									var content = $(this).html();
									if ($(this).hasClass("mathjax-inline")) {
										$(this).html("$ " + content + " $");
									} else {
										$(this).html("$$" + "\n" + content + "\n" + "$$");
									}

									if (i + 1 === c) {
										MathJax.Hub.Config({
											showProcessingMessages: false,
											messageStyle: "none",
											extensions: [
												"tex2jax.js",
												"TeX/mediawiki-texvc.js",
												"TeX/noUndefined.js",
												"TeX/autoload-all.js",
												"TeX/AMSmath.js",
												"TeX/AMSsymbols.js"
											],
											jax: [
												"input/TeX",
												"output/SVG"
											],
											elements: document.getElementsByClassName("language-mathjax"),
											tex2jax: {
												skipTags: [
													"script",
													"noscript",
													"style",
													"textarea"
												],
												inlineMath: [
													[\'$\', \'$\']
												],
												displayMath: [
													[\'$$\', \'$$\']
												],
												processClass: "language-mathjax"
											}
										});

										MathJax.Hub.Queue(["Typeset", MathJax.Hub]);

										$(".language-mathjax").attr("style", "background: transparent; border: 0;");
										$(".language-mathjax").closest("pre").attr("style", "background: transparent; border: 0;");
									}
								});
                            } else {
                                console.log("[wp-githuber-md] MathJax code blocks not found.");
                            }
                        } else {
                            console.log("[wp-githuber-md] MathJax is not loadded.");
                        }  
                    });
                })(jQuery);
            </script>
		';
		echo preg_replace( '/\s+/', ' ', $script );
	}

	/**
	 * MathJax Inline Markup
	 * 
	 * Ex.
	 * `$ x_{1,2} = {-b\pm\sqrt{b^2 - 4ac} \over 2a}. $`
	 *
	 * @param string  $content HTML or Markdown content.
	 * @return void
	 */
	public static function mathjax_inline_markup( $content ) {

		$regex = '%<code>\$((?:[^$]+ |(?<=(?<!\\\\)\\\\)\$ )+)(?<!\\\\)\$<\/code>%ix';
		$content = preg_replace_callback( $regex, function() {
			$matches = func_get_arg(0);

			if ( ! empty( $matches[1] ) ) {
				$mathjax = $matches[1];
				$mathjax = str_replace( array( '&lt;', '&gt;', '&quot;', '&#039;', '&#038;', '&amp;', "\n", "\r" ), array( '<', '>', '"', "'", '&', '&', ' ', ' ' ), $mathjax );
				return '<code class="mathjax-inline language-mathjax">' . trim( $mathjax ) . '</code>';
			}
		}, $content );

		return $content;
	}
}
Modules/MarkdownForComments.php000066600000000522151747671010012640 0ustar00<?php
/**
 * Module Name: Markdown for Comments
 * Module Description: Display a simple Markdown editor in comment area.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.12.0
 * @version 1.12.0
 */

namespace Githuber\Module;

/**
 * Todo
 */
class MardkwonForComments extends ModuleAbstract {

	
}
Modules/TaskList.php000066600000001552151747671010010443 0ustar00<?php
/**
 * Module Name: TaskList
 * Module Description: upport Github Flavored Markdown task lists.
 *
 * @author Terry Lin
 * @link https://terryl.in/
 *
 * @package Githuber
 * @since 1.0.0
 * @version 1.0.0
 */

namespace Githuber\Module;

class TaskList {

	/**
	 * Support Github Flavored Markdown task lists.
	 * 
	 * @param string $text HTML content.
	 * @return string filtered HTML content.
	 */
	public static function parse_gfm_task_list( $text ) {
		$checked_item   = '<li class="gfm-task-list"><input type="checkbox">$1$2';
		$unchecked_item = '<li class="gfm-task-list"><input type="checkbox" checked>$1$2';

		// Replace task-list signs to corresponding HTML code.
		$text = preg_replace( "#<li>\[\s\] (.*?)([</li>|<ul>])#", $checked_item, $text );
		$text = preg_replace( "#<li>\[[x]\] (.*?)([</li>|<ul>])#", $unchecked_item, $text );
		return $text;
	}
}