* Finish task #54205.
This commit is contained in:
25
lib/michelf/.github/workflows/ci.yml
vendored
Normal file
25
lib/michelf/.github/workflows/ci.yml
vendored
Normal 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
|
||||
36
lib/michelf/.scrutinizer.yml
Normal file
36
lib/michelf/.scrutinizer.yml
Normal 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
19
lib/michelf/.travis.yml
Normal 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
|
||||
10
lib/michelf/Michelf/Markdown.inc.php
Normal file
10
lib/michelf/Michelf/Markdown.inc.php
Normal 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';
|
||||
1889
lib/michelf/Michelf/Markdown.php
Normal file
1889
lib/michelf/Michelf/Markdown.php
Normal file
File diff suppressed because it is too large
Load Diff
11
lib/michelf/Michelf/MarkdownExtra.inc.php
Normal file
11
lib/michelf/Michelf/MarkdownExtra.inc.php
Normal 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';
|
||||
1870
lib/michelf/Michelf/MarkdownExtra.php
Normal file
1870
lib/michelf/Michelf/MarkdownExtra.php
Normal file
File diff suppressed because it is too large
Load Diff
9
lib/michelf/Michelf/MarkdownInterface.inc.php
Normal file
9
lib/michelf/Michelf/MarkdownInterface.inc.php
Normal 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';
|
||||
38
lib/michelf/Michelf/MarkdownInterface.php
Normal file
38
lib/michelf/Michelf/MarkdownInterface.php
Normal 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);
|
||||
}
|
||||
27
lib/michelf/michelf.class.php
Normal file
27
lib/michelf/michelf.class.php
Normal 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>";
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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' => '↩',
|
||||
'allowRawHtmlInSafeMode' => true,
|
||||
'autobreak' => false,
|
||||
);
|
||||
}
|
||||
|
||||
unset($backLinkElements[0]);
|
||||
|
||||
$n = count($textElements) -1;
|
||||
|
||||
if ($textElements[$n]['name'] === 'p')
|
||||
{
|
||||
$backLinkElements = array_merge(
|
||||
array(
|
||||
array(
|
||||
'rawHtml' => ' ',
|
||||
'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]+[ ]*)';
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user