* Finish task #54205.

This commit is contained in:
孙广明
2022-05-17 11:15:33 +08:00
parent c4e649ef25
commit 46e4eae295
15 changed files with 3937 additions and 3293 deletions

25
lib/michelf/.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: CI
on:
pull_request: null
push:
branches:
- lib
jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['7.4', '8.0']
name: Linting - PHP ${{ matrix.php }}
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
extensions: intl
- run: composer install --no-progress
# - run: composer codestyle
- run: composer phpstan
- run: composer tests

View File

@@ -0,0 +1,36 @@
build:
environment:
php:
version: '7.4'
nodes:
analysis:
project_setup:
override:
- 'true'
tests:
override:
-
command: 'vendor/bin/phpunit --coverage-clover=clover.xml'
coverage:
file: 'clover.xml'
format: 'clover'
-
command: phpcs-run
use_website_config: true
environment:
node:
version: 6.0.0
tests: true
filter:
excluded_paths:
- 'test/*'
checks:
php: true
coding_style:
php:
indentation:
general:
use_tabs: true
spaces:
around_operators:
concatenation: true

19
lib/michelf/.travis.yml Normal file
View File

@@ -0,0 +1,19 @@
language: php
matrix:
include:
- php: hhvm-3.18
dist: trusty
- php: 7.4
dist: bionic
- php: 8.0
dist: bionic
install:
- composer install --prefer-dist
script:
- vendor/bin/phpunit --log-junit=phpunit.log
notifications:
email: false

View File

@@ -0,0 +1,10 @@
<?php
// Use this file if you cannot use class autoloading. It will include all the
// files needed for the Markdown parser.
//
// Take a look at the PSR-0-compatible class autoloading implementation
// in the Readme.php file if you want a simple autoloader setup.
require_once dirname(__FILE__) . '/MarkdownInterface.php';
require_once dirname(__FILE__) . '/Markdown.php';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
<?php
// Use this file if you cannot use class autoloading. It will include all the
// files needed for the MarkdownExtra parser.
//
// Take a look at the PSR-0-compatible class autoloading implementation
// in the Readme.php file if you want a simple autoloader setup.
require_once dirname(__FILE__) . '/MarkdownInterface.php';
require_once dirname(__FILE__) . '/Markdown.php';
require_once dirname(__FILE__) . '/MarkdownExtra.php';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
<?php
// Use this file if you cannot use class autoloading. It will include all the
// files needed for the MarkdownInterface interface.
//
// Take a look at the PSR-0-compatible class autoloading implementation
// in the Readme.php file if you want a simple autoloader setup.
require_once dirname(__FILE__) . '/MarkdownInterface.php';

View File

@@ -0,0 +1,38 @@
<?php
/**
* Markdown - A text-to-HTML conversion tool for web writers
*
* @package php-markdown
* @author Michel Fortin <michel.fortin@michelf.com>
* @copyright 2004-2021 Michel Fortin <https://michelf.com/projects/php-markdown/>
* @copyright (Original Markdown) 2004-2006 John Gruber <https://daringfireball.net/projects/markdown/>
*/
namespace Michelf;
/**
* Markdown Parser Interface
*/
interface MarkdownInterface {
/**
* Initialize the parser and return the result of its transform method.
* This will work fine for derived classes too.
*
* @api
*
* @param string $text
* @return string
*/
public static function defaultTransform($text);
/**
* Main function. Performs some preprocessing on the input text
* and pass it through the document gamut.
*
* @api
*
* @param string $text
* @return string
*/
public function transform($text);
}

View File

@@ -0,0 +1,27 @@
<?php
require_once 'Michelf/MarkdownExtra.inc.php';
use Michelf\Markdown;
use Michelf\MarkdownExtra;
class michelf
{
/**
* Convert markdown to html parser.
*
* @param string $mdCodes
* @static
* @access public
* @return void
*/
public static function parse($mdCodes = '')
{
if(strlen($mdCodes) == 0) return '';
$html = Markdown::defaultTransform($mdCodes);
$parser = new MarkdownExtra;
$parser->fn_id_prefix = "post22-";
$html = $parser->transform($mdCodes);
return "<div class='markdown-print'>$html</div>";
}
}

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013-2018 Emanuil Rusev, erusev.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -1,686 +0,0 @@
<?php
#
#
# Parsedown Extra
# https://github.com/erusev/parsedown-extra
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, view the LICENSE file that was distributed
# with this source code.
#
#
class parsedownextra extends parsedown
{
# ~
const version = '0.8.0';
# ~
function __construct()
{
if (version_compare(parent::version, '1.7.1') < 0)
{
throw new Exception('ParsedownExtra requires a later version of Parsedown');
}
$this->BlockTypes[':'] []= 'DefinitionList';
$this->BlockTypes['*'] []= 'Abbreviation';
# identify footnote definitions before reference definitions
array_unshift($this->BlockTypes['['], 'Footnote');
# identify footnote markers before before links
array_unshift($this->InlineTypes['['], 'FootnoteMarker');
}
#
# ~
function text($text)
{
$Elements = $this->textElements($text);
# convert to markup
$markup = $this->elements($Elements);
# trim line breaks
$markup = trim($markup, "\n");
# merge consecutive dl elements
$markup = preg_replace('/<\/dl>\s+<dl>\s+/', '', $markup);
# add footnotes
if (isset($this->DefinitionData['Footnote']))
{
$Element = $this->buildFootnoteElement();
$markup .= "\n" . $this->element($Element);
}
return $markup;
}
#
# Blocks
#
#
# Abbreviation
protected function blockAbbreviation($Line)
{
if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/', $Line['text'], $matches))
{
$this->DefinitionData['Abbreviation'][$matches[1]] = $matches[2];
$Block = array(
'hidden' => true,
);
return $Block;
}
}
#
# Footnote
protected function blockFootnote($Line)
{
if (preg_match('/^\[\^(.+?)\]:[ ]?(.*)$/', $Line['text'], $matches))
{
$Block = array(
'label' => $matches[1],
'text' => $matches[2],
'hidden' => true,
);
return $Block;
}
}
protected function blockFootnoteContinue($Line, $Block)
{
if ($Line['text'][0] === '[' and preg_match('/^\[\^(.+?)\]:/', $Line['text']))
{
return;
}
if (isset($Block['interrupted']))
{
if ($Line['indent'] >= 4)
{
$Block['text'] .= "\n\n" . $Line['text'];
return $Block;
}
}
else
{
$Block['text'] .= "\n" . $Line['text'];
return $Block;
}
}
protected function blockFootnoteComplete($Block)
{
$this->DefinitionData['Footnote'][$Block['label']] = array(
'text' => $Block['text'],
'count' => null,
'number' => null,
);
return $Block;
}
#
# Definition List
protected function blockDefinitionList($Line, $Block)
{
if ( ! isset($Block) or $Block['type'] !== 'Paragraph')
{
return;
}
$Element = array(
'name' => 'dl',
'elements' => array(),
);
$terms = explode("\n", $Block['element']['handler']['argument']);
foreach ($terms as $term)
{
$Element['elements'] []= array(
'name' => 'dt',
'handler' => array(
'function' => 'lineElements',
'argument' => $term,
'destination' => 'elements'
),
);
}
$Block['element'] = $Element;
$Block = $this->addDdElement($Line, $Block);
return $Block;
}
protected function blockDefinitionListContinue($Line, array $Block)
{
if ($Line['text'][0] === ':')
{
$Block = $this->addDdElement($Line, $Block);
return $Block;
}
else
{
if (isset($Block['interrupted']) and $Line['indent'] === 0)
{
return;
}
if (isset($Block['interrupted']))
{
$Block['dd']['handler']['function'] = 'textElements';
$Block['dd']['handler']['argument'] .= "\n\n";
$Block['dd']['handler']['destination'] = 'elements';
unset($Block['interrupted']);
}
$text = substr($Line['body'], min($Line['indent'], 4));
$Block['dd']['handler']['argument'] .= "\n" . $text;
return $Block;
}
}
#
# Header
protected function blockHeader($Line)
{
$Block = parent::blockHeader($Line);
if ($Block !== null && preg_match('/[ #]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['handler']['argument'], $matches, PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] = $this->parseAttributeData($attributeString);
$Block['element']['handler']['argument'] = substr($Block['element']['handler']['argument'], 0, $matches[0][1]);
}
return $Block;
}
#
# Markup
protected function blockMarkup($Line)
{
if ($this->markupEscaped or $this->safeMode)
{
return;
}
if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
{
$element = strtolower($matches[1]);
if (in_array($element, $this->textLevelElements))
{
return;
}
$Block = array(
'name' => $matches[1],
'depth' => 0,
'element' => array(
'rawHtml' => $Line['text'],
'autobreak' => true,
),
);
$length = strlen($matches[0]);
$remainder = substr($Line['text'], $length);
if (trim($remainder) === '')
{
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
{
$Block['closed'] = true;
$Block['void'] = true;
}
}
else
{
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
{
return;
}
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
{
$Block['closed'] = true;
}
}
return $Block;
}
}
protected function blockMarkupContinue($Line, array $Block)
{
if (isset($Block['closed']))
{
return;
}
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
{
$Block['depth'] ++;
}
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
{
if ($Block['depth'] > 0)
{
$Block['depth'] --;
}
else
{
$Block['closed'] = true;
}
}
if (isset($Block['interrupted']))
{
$Block['element']['rawHtml'] .= "\n";
unset($Block['interrupted']);
}
$Block['element']['rawHtml'] .= "\n".$Line['body'];
return $Block;
}
protected function blockMarkupComplete($Block)
{
if ( ! isset($Block['void']))
{
$Block['element']['rawHtml'] = $this->processTag($Block['element']['rawHtml']);
}
return $Block;
}
#
# Setext
protected function blockSetextHeader($Line, array $Block = null)
{
$Block = parent::blockSetextHeader($Line, $Block);
if ($Block !== null && preg_match('/[ ]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['handler']['argument'], $matches, PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] = $this->parseAttributeData($attributeString);
$Block['element']['handler']['argument'] = substr($Block['element']['handler']['argument'], 0, $matches[0][1]);
}
return $Block;
}
#
# Inline Elements
#
#
# Footnote Marker
protected function inlineFootnoteMarker($Excerpt)
{
if (preg_match('/^\[\^(.+?)\]/', $Excerpt['text'], $matches))
{
$name = $matches[1];
if ( ! isset($this->DefinitionData['Footnote'][$name]))
{
return;
}
$this->DefinitionData['Footnote'][$name]['count'] ++;
if ( ! isset($this->DefinitionData['Footnote'][$name]['number']))
{
$this->DefinitionData['Footnote'][$name]['number'] = ++ $this->footnoteCount; # » &
}
$Element = array(
'name' => 'sup',
'attributes' => array('id' => 'fnref'.$this->DefinitionData['Footnote'][$name]['count'].':'.$name),
'element' => array(
'name' => 'a',
'attributes' => array('href' => '#fn:'.$name, 'class' => 'footnote-ref'),
'text' => $this->DefinitionData['Footnote'][$name]['number'],
),
);
return array(
'extent' => strlen($matches[0]),
'element' => $Element,
);
}
}
private $footnoteCount = 0;
#
# Link
protected function inlineLink($Excerpt)
{
$Link = parent::inlineLink($Excerpt);
$remainder = $Link !== null ? substr($Excerpt['text'], $Link['extent']) : '';
if (preg_match('/^[ ]*{('.$this->regexAttribute.'+)}/', $remainder, $matches))
{
$Link['element']['attributes'] += $this->parseAttributeData($matches[1]);
$Link['extent'] += strlen($matches[0]);
}
return $Link;
}
#
# ~
#
private $currentAbreviation;
private $currentMeaning;
protected function insertAbreviation(array $Element)
{
if (isset($Element['text']))
{
$Element['elements'] = self::pregReplaceElements(
'/\b'.preg_quote($this->currentAbreviation, '/').'\b/',
array(
array(
'name' => 'abbr',
'attributes' => array(
'title' => $this->currentMeaning,
),
'text' => $this->currentAbreviation,
)
),
$Element['text']
);
unset($Element['text']);
}
return $Element;
}
protected function inlineText($text)
{
$Inline = parent::inlineText($text);
if (isset($this->DefinitionData['Abbreviation']))
{
foreach ($this->DefinitionData['Abbreviation'] as $abbreviation => $meaning)
{
$this->currentAbreviation = $abbreviation;
$this->currentMeaning = $meaning;
$Inline['element'] = $this->elementApplyRecursiveDepthFirst(
array($this, 'insertAbreviation'),
$Inline['element']
);
}
}
return $Inline;
}
#
# Util Methods
#
protected function addDdElement(array $Line, array $Block)
{
$text = substr($Line['text'], 1);
$text = trim($text);
unset($Block['dd']);
$Block['dd'] = array(
'name' => 'dd',
'handler' => array(
'function' => 'lineElements',
'argument' => $text,
'destination' => 'elements'
),
);
if (isset($Block['interrupted']))
{
$Block['dd']['handler']['function'] = 'textElements';
unset($Block['interrupted']);
}
$Block['element']['elements'] []= & $Block['dd'];
return $Block;
}
protected function buildFootnoteElement()
{
$Element = array(
'name' => 'div',
'attributes' => array('class' => 'footnotes'),
'elements' => array(
array('name' => 'hr'),
array(
'name' => 'ol',
'elements' => array(),
),
),
);
uasort($this->DefinitionData['Footnote'], 'self::sortFootnotes');
foreach ($this->DefinitionData['Footnote'] as $definitionId => $DefinitionData)
{
if ( ! isset($DefinitionData['number']))
{
continue;
}
$text = $DefinitionData['text'];
$textElements = parent::textElements($text);
$numbers = range(1, $DefinitionData['count']);
$backLinkElements = array();
foreach ($numbers as $number)
{
$backLinkElements[] = array('text' => ' ');
$backLinkElements[] = array(
'name' => 'a',
'attributes' => array(
'href' => "#fnref$number:$definitionId",
'rev' => 'footnote',
'class' => 'footnote-backref',
),
'rawHtml' => '&#8617;',
'allowRawHtmlInSafeMode' => true,
'autobreak' => false,
);
}
unset($backLinkElements[0]);
$n = count($textElements) -1;
if ($textElements[$n]['name'] === 'p')
{
$backLinkElements = array_merge(
array(
array(
'rawHtml' => '&#160;',
'allowRawHtmlInSafeMode' => true,
),
),
$backLinkElements
);
unset($textElements[$n]['name']);
$textElements[$n] = array(
'name' => 'p',
'elements' => array_merge(
array($textElements[$n]),
$backLinkElements
),
);
}
else
{
$textElements[] = array(
'name' => 'p',
'elements' => $backLinkElements
);
}
$Element['elements'][1]['elements'] []= array(
'name' => 'li',
'attributes' => array('id' => 'fn:'.$definitionId),
'elements' => array_merge(
$textElements
),
);
}
return $Element;
}
# ~
protected function parseAttributeData($attributeString)
{
$Data = array();
$attributes = preg_split('/[ ]+/', $attributeString, - 1, PREG_SPLIT_NO_EMPTY);
foreach ($attributes as $attribute)
{
if ($attribute[0] === '#')
{
$Data['id'] = substr($attribute, 1);
}
else # "."
{
$classes []= substr($attribute, 1);
}
}
if (isset($classes))
{
$Data['class'] = implode(' ', $classes);
}
return $Data;
}
# ~
protected function processTag($elementMarkup) # recursive
{
# http://stackoverflow.com/q/1148928/200145
libxml_use_internal_errors(true);
$DOMDocument = new DOMDocument;
# http://stackoverflow.com/q/11309194/200145
$elementMarkup = mb_convert_encoding($elementMarkup, 'HTML-ENTITIES', 'UTF-8');
# http://stackoverflow.com/q/4879946/200145
$DOMDocument->loadHTML($elementMarkup);
$DOMDocument->removeChild($DOMDocument->doctype);
$DOMDocument->replaceChild($DOMDocument->firstChild->firstChild->firstChild, $DOMDocument->firstChild);
$elementText = '';
if ($DOMDocument->documentElement->getAttribute('markdown') === '1')
{
foreach ($DOMDocument->documentElement->childNodes as $Node)
{
$elementText .= $DOMDocument->saveHTML($Node);
}
$DOMDocument->documentElement->removeAttribute('markdown');
$elementText = "\n".$this->text($elementText)."\n";
}
else
{
foreach ($DOMDocument->documentElement->childNodes as $Node)
{
$nodeMarkup = $DOMDocument->saveHTML($Node);
if ($Node instanceof DOMElement and ! in_array($Node->nodeName, $this->textLevelElements))
{
$elementText .= $this->processTag($nodeMarkup);
}
else
{
$elementText .= $nodeMarkup;
}
}
}
# because we don't want for markup to get encoded
$DOMDocument->documentElement->nodeValue = 'placeholder\x1A';
$markup = $DOMDocument->saveHTML($DOMDocument->documentElement);
$markup = str_replace('placeholder\x1A', $elementText, $markup);
return $markup;
}
# ~
protected function sortFootnotes($A, $B) # callback
{
return $A['number'] - $B['number'];
}
#
# Fields
#
protected $regexAttribute = '(?:[#.][-\w]+[ ]*)';
}

View File

@@ -1,590 +0,0 @@
<?php
#
#
# Parsedown Extra Plugin
# https://github.com/taufik-nurrohman/parsedown-extra-plugin
#
# (c) Emanuil Rusev
# http://erusev.com
#
# (c) Taufik Nurrohman
# https://mecha-cms.com
#
# For the full license information, view the LICENSE file that was distributed
# with this source code.
#
#
require 'parsedown.php';
require 'parsedownextra.php';
class parsedownextraplugin extends parsedownextra {
const version = '1.3.6';
public $abbreviationData = array();
public $blockCodeAttributes = array();
public $blockCodeClassFormat = 'language-%s';
public $blockCodeHtml = null;
public $blockQuoteAttributes = array();
public $blockQuoteText = null;
public $codeAttributes = array();
public $codeAttributesOnParent = false;
public $codeHtml = null;
public $figureAttributes = array();
public $figuresEnabled = false;
public $footnoteAttributes = array();
public $footnoteBackLinkAttributes = array();
public $footnoteBackLinkHtml = null;
public $footnoteBackReferenceAttributes = array();
public $footnoteLinkAttributes = array();
public $footnoteLinkHtml = null;
public $footnoteReferenceAttributes = array();
public $headerAttributes = array();
public $headerText = null;
public $imageAttributes = array();
public $imageAttributesOnParent = false;
public $linkAttributes = array();
public $referenceData = array();
public $tableAttributes = array();
public $tableColumnAttributes = array();
public $voidElementSuffix = ' />';
protected $regexAttribute = '(?:[#.][-\w:\\\]+[ ]*|[-\w:\\\]+(?:=(?:["\'][^\n]*?["\']|[^\s]+)?)?[ ]*)';
# Method aliases for every configuration property
public function __call($key, array $arguments = array()) {
$property = lcfirst(substr($key, 3));
if (strpos($key, 'set') === 0 && property_exists($this, $property)) {
$this->{$property} = $arguments[0];
return $this;
}
throw new Exception('Method ' . $key . ' does not exists.');
}
public function __construct() {
if (version_compare(parent::version, '0.8.0-beta-1') < 0) {
throw new Exception('ParsedownExtraPlugin requires a later version of Parsedown');
}
$this->BlockTypes['!'][] = 'Image';
parent::__construct();
}
protected function blockAbbreviation($Line) {
// Allow empty abbreviations
if (preg_match('/^\*\[(.+?)\]:[ ]*$/', $Line['text'], $matches)) {
$this->DefinitionData['Abbreviation'][$matches[1]] = null;
return array('hidden' => true);
}
return parent::blockAbbreviation($Line);
}
protected function blockCodeComplete($Block) {
$this->doSetAttributes($Block['element']['element'], $this->blockCodeAttributes);
$this->doSetContent($Block['element']['element'], $this->blockCodeHtml, true);
// Put code attributes on parent element
if ($this->codeAttributesOnParent) {
if ($this->codeAttributesOnParent === true) {
// $this->codeAttributesOnParent = array_keys($Block['element']['element']['attributes']);
$this->codeAttributesOnParent = array('class', 'id');
}
foreach ((array) $this->codeAttributesOnParent as $Name) {
if (isset($Block['element']['element']['attributes'][$Name])) {
$Block['element']['attributes'][$Name] = $Block['element']['element']['attributes'][$Name];
unset($Block['element']['element']['attributes'][$Name]);
}
}
}
$Block['element']['element']['rawHtml'] = $Block['element']['element']['text'];
$Block['element']['element']['allowRawHtmlInSafeMode'] = true;
unset($Block['element']['element']['text']);
return $Block;
}
protected function blockFencedCode($Line) {
// Re-enable the multiple class name feature
$Line['text'] = strtr(trim($Line['text']), array(
' ' => "\x1A",
'.' => "\x1A."
));
// Enable custom attribute syntax on code block
$Attributes = array();
if (strpos($Line['text'], '{') !== false && substr($Line['text'], -1) === '}') {
$Parts = explode('{', $Line['text'], 2);
$Attributes = $this->parseAttributeData(strtr(substr($Parts[1], 0, -1), "\x1A", ' '));
$Line['text'] = trim($Parts[0]);
}
if (!$Block = parent::blockFencedCode($Line)) {
return;
}
if ($Attributes) {
$Block['element']['element']['attributes'] = $Attributes;
} else if (isset($Block['element']['element']['attributes']['class'])) {
$Classes = explode("\x1A", strtr($Block['element']['element']['attributes']['class'], ' ', "\x1A"));
// `~~~ php` → `<pre><code class="language-php">`
// `~~~ php html` → `<pre><code class="language-php language-html">`
// `~~~ .php` → `<pre><code class="php">`
// `~~~ .php.html` → `<pre><code class="php html">`
// `~~~ .php html` → `<pre><code class="php language-html">`
// `~~~ {.php #foo}` → `<pre><code id="foo" class="php">`
$Results = [];
foreach ($Classes as $Class) {
if ($Class === "" || $Class === strtr($this->blockCodeClassFormat, array('%s' => ""))) {
continue;
}
if ($Class[0] === '.') {
$Results[] = substr($Class, 1);
} else if (preg_match('/^' . strtr(preg_quote($this->blockCodeClassFormat), array('%s' => '\S+')) . '$/', $Class)) {
$Results[] = $Class; // Do nothing!
} else {
$Results[] = sprintf($this->blockCodeClassFormat, $Class);
}
}
if ($Results = array_unique($Results)) {
$Block['element']['element']['attributes']['class'] = implode(' ', $Results);
} else {
unset($Block['element']['element']['attributes']['class']);
}
}
return $Block;
}
protected function blockFencedCodeComplete($Block) {
return $this->blockCodeComplete($Block);
}
protected function blockHeader($Line) {
if (!$Block = parent::blockHeader($Line)) {
return;
}
$Level = strspn($Line['text'], '#');
$this->doSetAttributes($Block['element'], $this->headerAttributes, array($Level));
$this->doSetContent($Block['element'], $this->headerText, false, 'argument', array($Level));
return $Block;
}
protected function blockImage($Line) {
if (!$this->figuresEnabled) {
return;
}
// Match exactly an image syntax in a paragraph (with optional custom attributes, and optional hard break marker)
if (preg_match('/^\!\[[^\n]*?\](\[[^\n]*?\]|\([^\n]*?\))(\s*\{' . $this->regexAttribute . '+?\})?([ ]{2})?$/', $Line['text'])) {
$Block = array(
'description' => "",
'element' => array(
'name' => 'figure',
'attributes' => array(),
'elements' => array(
$this->inlineImage($Line)
)
)
);
$this->doSetAttributes($Block['element'], $this->figureAttributes);
return $Block;
}
return;
}
protected function blockImageComplete($Block) {
if (!empty($Block['description'])) {
$Description = $Block['description'];
$Block['element']['elements'][] = array(
'name' => 'figcaption',
'rawHtml' => $this->{strpos($Description, "\n\n") === false ? 'line' : 'text'}(trim($Description, "\n"))
);
// unset($Block['description']);
}
if ($this->imageAttributesOnParent) {
$Inline = $Block['element']['elements'][0];
if ($this->imageAttributesOnParent === true) {
$this->imageAttributesOnParent = array_keys($Inline['element']['attributes']);
}
foreach ((array) $this->imageAttributesOnParent as $Name) {
if (isset($Inline['element']['attributes'][$Name])) {
// Merge class names
if (
$Name === 'class' &&
isset($Block['element']['attributes'][$Name]) &&
isset($Inline['element']['attributes'][$Name])
) {
$Classes = array_merge(
explode(' ', $Block['element']['attributes'][$Name]),
explode(' ', $Inline['element']['attributes'][$Name])
);
sort($Classes);
$Block['element']['attributes']['class'] = implode(' ', array_unique(array_filter($Classes)));
unset($Block['element']['elements'][0]['element']['attributes'][$Name]);
continue;
}
$Block['element']['attributes'][$Name] = $Inline['element']['attributes'][$Name];
unset($Block['element']['elements'][0]['element']['attributes'][$Name]);
}
}
}
return $Block;
}
protected function blockImageContinue($Line, array $Block) {
if (isset($Block['complete'])) {
return;
}
if (isset($Block['interrupted'])) {
$Block['description'] .= "\n";
unset($Block['interrupted']);
}
if ($Line['indent'] === 0) {
$Block['complete'] = true;
return;
}
if ($Line['indent'] > 0 && $Line['indent'] < 4) {
$Block['description'] .= "\n" . $Line['text'];
return $Block;
}
return;
}
protected function blockQuoteComplete($Block) {
$this->doSetAttributes($Block['element'], $this->blockQuoteAttributes);
$this->doSetContent($Block['element'], $this->blockQuoteText, false, 'arguments');
return $Block;
}
protected function blockSetextHeader($Line, array $Block = null) {
if (!$Block = parent::blockSetextHeader($Line, $Block)) {
return;
}
$Level = $Line['text'][0] === '=' ? 1 : 2;
$this->doSetAttributes($Block['element'], $this->headerAttributes, array($Level));
$this->doSetContent($Block['element'], $this->headerText, false, 'argument', array($Level));
return $Block;
}
protected function blockTableContinue($Line, array $Block) {
if (!$Block = parent::blockTableContinue($Line, $Block)) {
return;
}
$Aligns = $Block['alignments'];
// `<thead>` or `<tbody>`
foreach ($Block['element']['elements'] as $Index0 => &$Element0) {
// `<tr>`
foreach ($Element0['elements'] as $Index1 => &$Element1) {
// `<th>` or `<td>`
foreach ($Element1['elements'] as $Index2 => &$Element2) {
$this->doSetAttributes($Element2, $this->tableColumnAttributes, array($Aligns[$Index2], $Index2, $Index1));
}
}
}
return $Block;
}
protected function blockTableComplete($Block) {
$this->doSetAttributes($Block['element'], $this->tableAttributes);
return $Block;
}
protected function buildFootnoteElement() {
$DefinitionData = $this->DefinitionData['Footnote'];
if (!$Footnotes = parent::buildFootnoteElement()) {
return;
}
$DefinitionKey = array_keys($DefinitionData);
$DefinitionData = array_values($DefinitionData);
$this->doSetAttributes($Footnotes, $this->footnoteAttributes);
foreach ($Footnotes['elements'][1]['elements'] as $Index0 => &$Element0) {
$Name = $DefinitionKey[$Index0];
$Count = $DefinitionData[$Index0]['count'];
$Args = array(is_numeric($Name) ? (float) $Name : $Name, $Count);
$this->doSetAttributes($Element0, $this->footnoteBackReferenceAttributes, $Args);
foreach ($Element0['elements'] as $Index1 => &$Element1) {
if (!isset($Element1['elements'])) {
continue;
}
$Count = 0;
foreach ($Element1['elements'] as $Index2 => &$Element2) {
if (!isset($Element2['name']) || $Element2['name'] !== 'a') {
continue;
}
$Args[1] = ++$Count;
$this->doSetAttributes($Element2, $this->footnoteBackLinkAttributes, $Args);
$this->doSetContent($Element2, $this->footnoteBackLinkHtml, false, 'rawHtml');
}
}
}
return $Footnotes;
}
protected function doGetAttributes($Element) {
if (isset($Element['attributes'])) {
return (array) $Element['attributes'];
}
return array();
}
protected function doGetContent($Element) {
if (isset($Element['text'])) {
return $Element['text'];
}
if (isset($Element['rawHtml'])) {
return $Element['rawHtml'];
}
if (isset($Element['handler']['argument'])) {
return implode("\n", (array) $Element['handler']['argument']);
}
return null;
}
private function doSetLink($Excerpt, $Function) {
if (!$Inline = call_user_func('parent::' . $Function, $Excerpt)) {
return;
}
$this->doSetAttributes($Inline['element'], $this->linkAttributes, array($this->isLocal($Inline['element'], 'href')));
$this->doSetData($this->DefinitionData['Reference'], $this->referenceData);
return $Inline;
}
protected function doSetAttributes(&$Element, $From, $Args = array()) {
$Attributes = $this->doGetAttributes($Element);
$Content = $this->doGetContent($Element);
if (is_callable($From)) {
$Args = array_merge(array($Content, $Attributes, &$Element), $Args);
$Element['attributes'] = array_replace($Attributes, (array) call_user_func_array($From, $Args));
} else {
$Element['attributes'] = array_replace($Attributes, (array) $From);
}
}
protected function doSetContent(&$Element, $From, $Esc = false, $Mode = 'text', $Args = array()) {
$Attributes = $this->doGetAttributes($Element);
$Content = $this->doGetContent($Element);
if ($Esc) {
$Content = parent::escape($Content, true);
}
if (is_callable($From)) {
$Args = array_merge(array($Content, $Attributes, &$Element), $Args);
$Content = call_user_func_array($From, $Args);
} else if (!empty($From)) {
$Content = sprintf($From, $Content);
}
if ($Mode === 'arguments') {
$Element['handler']['argument'] = explode("\n", $Content);
} else if ($Mode === 'argument') {
$Element['handler']['argument'] = $Content;
} else {
$Element[$Mode] = $Content;
}
}
protected function doSetData(&$To, $From) {
$To = array_replace((array) $To, (array) $From);
}
protected function element(array $Element) {
if (!$Any = parent::element($Element)) {
return;
}
if (substr($Any, -3) === ' />') {
if (is_callable($this->voidElementSuffix)) {
$Attributes = $this->doGetAttributes($Element);
$Content = $this->doGetContent($Element);
$Suffix = call_user_func_array($this->voidElementSuffix, [$Content, $Attributes, &$Element]);
} else {
$Suffix = $this->voidElementSuffix;
}
$Any = substr_replace($Any, $Suffix, -3);
}
return $Any;
}
protected function inlineCode($Excerpt) {
if (!$Inline = parent::inlineCode($Excerpt)) {
return;
}
$this->doSetAttributes($Inline['element'], $this->codeAttributes);
$this->doSetContent($Inline['element'], $this->codeHtml, true);
$Inline['element']['rawHtml'] = $Inline['element']['text'];
$Inline['element']['allowRawHtmlInSafeMode'] = true;
unset($Inline['element']['text']);
return $Inline;
}
protected function inlineFootnoteMarker($Excerpt) {
if (!$Inline = parent::inlineFootnoteMarker($Excerpt)) {
return;
}
$Name = null;
if (preg_match('/^\[\^(.+?)\]/', $Excerpt['text'], $matches)) {
$Name = $matches[1];
}
$Args = array(is_numeric($Name) ? (float) $Name : $Name, $this->DefinitionData['Footnote'][$Name]['count']);
$this->doSetAttributes($Inline['element'], $this->footnoteReferenceAttributes, $Args);
$this->doSetAttributes($Inline['element']['element'], $this->footnoteLinkAttributes, $Args);
$this->doSetContent($Inline['element']['element'], $this->footnoteLinkHtml, false, 'text', $Args);
$Inline['element']['element']['rawHtml'] = $Inline['element']['element']['text'];
$Inline['element']['element']['allowRawHtmlInSafeMode'] = true;
unset($Inline['element']['element']['text']);
return $Inline;
}
protected function inlineImage($Excerpt) {
if (!$Inline = parent::inlineImage($Excerpt)) {
return;
}
$this->doSetAttributes($Inline['element'], $this->imageAttributes, array($this->isLocal($Inline['element'], 'src')));
return $Inline;
}
protected function inlineLink($Excerpt) {
return $this->doSetLink($Excerpt, __FUNCTION__);
}
protected function inlineText($Text) {
$this->doSetData($this->DefinitionData['Abbreviation'], $this->abbreviationData);
return parent::inlineText($Text);
}
protected function inlineUrl($Excerpt) {
return $this->doSetLink($Excerpt, __FUNCTION__);
}
protected function inlineUrlTag($Excerpt) {
return $this->doSetLink($Excerpt, __FUNCTION__);
}
protected function isLocal($Element, $Key) {
$Link = isset($Element['attributes'][$Key]) ? (string) $Element['attributes'][$Key] : null;
if (
// `<a href="">`
$Link === "" ||
// `<a href="../foo/bar">`
// `<a href="/foo/bar">`
// `<a href="?foo=bar">`
// `<a href="&foo=bar">`
// `<a href="#foo">`
strpos('./?&#', $Link[0]) !== false && strpos($Link, '//') !== 0 ||
// `<a href="data:text/html,asdf">`
strpos($Link, 'data:') === 0 ||
// `<a href="javascript:;">`
strpos($Link, 'javascript:') === 0 ||
// `<a href="mailto:as@df">`
strpos($Link, 'mailto:') === 0
) {
return true;
}
if (isset($_SERVER['HTTP_HOST'])) {
$Host = $_SERVER['HTTP_HOST'];
} else if (isset($_SERVER['SERVER_NAME'])) {
$Host = $_SERVER['SERVER_NAME'];
} else {
$Host = "";
}
// `<a href="//example.com">`
if (strpos($Link, '//') === 0 && strpos($Link, '//' . $Host) !== 0) {
return false;
}
if (
// `<a href="https://127.0.0.1">`
strpos($Link, 'https://' . $Host) === 0 ||
// `<a href="http://127.0.0.1">`
strpos($Link, 'http://' . $Host) === 0
) {
return true;
}
// `<a href="foo/bar">`
return strpos($Link, '://') === false;
}
protected function parseAttributeData($attributeString) {
// Allow compact attributes
$attributeString = strtr($attributeString, array(
'#' => ' #',
'.' => ' .'
));
if (strpos($attributeString, '="') !== false || strpos($attributeString, "='") !== false) {
$attributeString = preg_replace_callback('#([-\w]+=)(["\'])([^\n]*?)\2#', function($matches) {
$value = strtr($matches[3], array(
' #' => '#',
' .' => '.',
' ' => "\x1A"
));
return $matches[1] . $matches[2] . $value . $matches[2];
}, $attributeString);
}
$Attributes = array();
foreach (explode(' ', $attributeString) as $v) {
if (!$v) {
continue;
}
// `{#foo}`
if ($v[0] === '#' && isset($v[1])) {
$Attributes['id'] = substr($v, 1);
// `{.foo}`
} else if ($v[0] === '.' && isset($v[1])) {
$Attributes['class'][] = substr($v, 1);
// ~
} else if (strpos($v, '=') !== false) {
$vv = explode('=', $v, 2);
// `{foo=}`
if ($vv[1] === "") {
if ($vv[0] === 'class') {
continue;
}
$Attributes[$vv[0]] = "";
// `{foo="bar baz"}`
// `{foo='bar baz'}`
} else if ($vv[1][0] === '"' && substr($vv[1], -1) === '"' || $vv[1][0] === "'" && substr($vv[1], -1) === "'") {
$values = stripslashes(strtr(substr(substr($vv[1], 1), 0, -1), "\x1A", ' '));
if ($vv[0] === 'class' && isset($Attributes[$vv[0]])) {
$values = explode(' ', $values);
$Attributes[$vv[0]] = array_merge($Attributes[$vv[0]], $values);
} else {
$Attributes[$vv[0]] = $values;
}
// `{foo=bar}`
} else {
if ($vv[0] === 'class' && isset($Attributes[$vv[0]])) {
$Attributes[$vv[0]] = array_merge($Attributes[$vv[0]], [$vv[1]]);
} else {
$Attributes[$vv[0]] = $vv[1];
}
}
// `{foo}`
} else {
if ($v === 'class' && isset($Attributes[$v])) {
continue;
}
$Attributes[$v] = $v;
}
}
if (isset($Attributes['class'])) {
$Attributes['class'] = implode(' ', array_unique((array) $Attributes['class']));
}
return $Attributes;
}
}

View File

@@ -3250,13 +3250,11 @@ EOD;
if(empty($markdown)) return false;
global $app;
$app->loadClass('parsedownextraplugin');
$app->loadClass('michelf');
$Parsedown = new parsedownextraplugin;
$Michelf = new michelf;
$Parsedown->voidElementSuffix = '>'; // HTML5
return $Parsedown->text($markdown);
return $Michelf->parse($markdown);
}
}