mirror of
https://github.com/suk-ws/ph-Bookshelf.git
synced 2024-12-05 09:26:52 +08:00
使用 composer, markdown 解析器切换为 commonmark。添加脚注&任务表的 bread-card 样式。
- 使用 composer - 声明 php8, ext-xml, ext-mbstring - 添加了 league/commonmark>=2.3.8 - 删除了 /lib 文件夹 (以及 parsedown 依赖) - 抽象化 md 解析代码同时 md 解析器换为 commonmark - 使用 commonmark 规范 + gfm - disallowed html + attributes, footnote, description list - 修改 readme
This commit is contained in:
parent
594a695cfb
commit
e8fe17b673
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,6 +4,9 @@ debug/
|
||||
.idea/
|
||||
|
||||
# 忽略服务器运行时文件
|
||||
vendor/
|
||||
composer.lock
|
||||
composer.phar
|
||||
.well-known/
|
||||
.user.ini
|
||||
|
||||
|
28
README.md
28
README.md
@ -11,17 +11,23 @@
|
||||
|
||||
<br/>
|
||||
|
||||
### 安装
|
||||
## 安装
|
||||
|
||||
下载/clone此仓库的内容,然后拖进 php 站点根目录。
|
||||
下载/clone此仓库的内容,然后拖进 php 站点根目录即可。
|
||||
|
||||
**要求 php 环境安装了 php-xml 插件**
|
||||
(7.0及以下旧版本可能叫做 php-dom 插件)
|
||||
(安装方法应该是能 Google 到的)
|
||||
### web-server 环境要求
|
||||
|
||||
对于 Apache(即 .htaccess 支持的 php 环境),可以直接运行。
|
||||
|
||||
对于 Nginx 或者别的之类的 php 环境,需要转换一下伪静态配置,以 .htaccess 文件内的内容为依据即可。
|
||||
- 支援 `.htaccess` 的 Webserver
|
||||
- 如果使用 Apache:
|
||||
- 启用模块 `rewrite`
|
||||
- 为网站根目录设置 `AllowOverride All`
|
||||
- 使用其它 Webserver,可以自行查询如何将 .htaccess 规则转换为你所使用的网站配置并写进你的网站配置当中
|
||||
- PHP 版本 8.0 以上
|
||||
(旧版可能可以使用,但未经完全测试)
|
||||
- PHP 模块 `xml` (旧版可能叫做 `dom`)
|
||||
- PHP 模块 `mbstring`
|
||||
- composer 工具以安装项目依赖
|
||||
- 在 php.ini 中设置 `display_errors` 以及 `display_startup_errors` 为 `Off` (或者关闭 `E_WARNING` 及以下 log) <small>(这是由于最开始写代码极不上心导致很多地方都会有可能报出 warn,输出在屏幕上会导致很糟糕的使用体验)</small>
|
||||
|
||||
<br/>
|
||||
|
||||
@ -31,8 +37,6 @@
|
||||
|
||||
<br/>
|
||||
|
||||
### 开源许可
|
||||
## 开源许可
|
||||
|
||||
项目自身:MIT License.
|
||||
|
||||
LIB: ParseDown : MIT License.
|
||||
MIT License.
|
||||
|
30
assets/bread-card-markdown-footnote.css
Normal file
30
assets/bread-card-markdown-footnote.css
Normal file
@ -0,0 +1,30 @@
|
||||
/******************************************************************************
|
||||
##############################################################################
|
||||
##### #####
|
||||
##### Markdown StyleSheet of ui design BreadCard #####
|
||||
##### extended support #####
|
||||
##### for footnote[^1] #####
|
||||
##### #####
|
||||
##### @author: Sukazyo Workshop #####
|
||||
##### @version: 1.0 #####
|
||||
##### #####
|
||||
##############################################################################
|
||||
******************************************************************************/
|
||||
|
||||
.footnote-ref {
|
||||
display: inline-block;
|
||||
border-style: solid;
|
||||
border-width: 0.15em;
|
||||
padding: 0 0.2em;
|
||||
transform: scale(0.6) translate(-0.4em, -0.4em);
|
||||
margin: 0 -0.5em;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.footnote::marker {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.footnote .footnote-backref {
|
||||
font-size: smaller;
|
||||
}
|
@ -2,8 +2,16 @@
|
||||
##############################################################################
|
||||
##### #####
|
||||
##### Markdown StyleSheet of ui design BreadCard #####
|
||||
##### extended support #####
|
||||
##### for task list - [x] y #####
|
||||
##### #####
|
||||
##### @author: Sukazyo Workshop #####
|
||||
##### @version: 1.0 #####
|
||||
##### #####
|
||||
##############################################################################
|
||||
******************************************************************************/
|
||||
|
||||
:is(ul, ol):has( > li > input[type="checkbox"]:disabled ) {
|
||||
list-style-type: none;
|
||||
padding-inline-start: 0;
|
||||
}
|
20
composer.json
Normal file
20
composer.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "suk-ws/ph-bookshelf",
|
||||
"description": "一个简单简洁(?)的文档书架实现。",
|
||||
"keywords": ["website", "documentation-tool", "markdown"],
|
||||
"minimum-stability": "alpha",
|
||||
"type": "website",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "A.C.Sukazyo Eyre",
|
||||
"email": "email@example.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"ext-xml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"league/commonmark": ">=2.3.8"
|
||||
}
|
||||
}
|
@ -2,6 +2,6 @@
|
||||
|
||||
const APP_NAME = "ph-Bookshelf";
|
||||
|
||||
const VERSION = "0.3.0.14";
|
||||
const VERSION = "0.3.0.15";
|
||||
const CHANNEL = "suk-ws";
|
||||
const BRANCH = "master";
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
require_once "./vendor/autoload.php";
|
||||
|
||||
require_once "./src/Data/SiteMeta.php";
|
||||
require_once "./src/Data/PageMeta.php";
|
||||
@ -13,7 +14,7 @@ try {
|
||||
SiteMeta::load();
|
||||
|
||||
// 格式化所给链接,并将链接转化为路径字符串数组
|
||||
$req = $_GET['p'];
|
||||
$req = array_key_exists('p', $_GET) ? $_GET['p'] : "";
|
||||
if (strlen($req) > 0 && $req[strlen($req) - 1] === '/')
|
||||
$tmp = substr($req, 0, -1);
|
||||
$uri = explode("/", $req, 2);
|
||||
|
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]+[ ]*)';
|
||||
}
|
@ -42,6 +42,8 @@ class SiteMeta {
|
||||
(PageMeta::getConfigurationLevelPage("customization.article.regex.highlight")=="false")?
|
||||
null:"//cdn.jsdelivr.net/gh/suk-ws/regex-colorizer@master/regex-colorizer-default.min.css",
|
||||
"/assets/bread-card-markdown.css?ver=1",
|
||||
"/assets/bread-card-markdown-footnote.css",
|
||||
"/assets/bread-card-markdown-task-list.css",
|
||||
(PageMeta::getConfigurationLevelPage("customization.article.listing.rainbow.marker")=="true"?
|
||||
"/assets/bread-card-markdown-enhanced-listing-rainbow.css?ver=1":null),
|
||||
"/assets/bread-card-markdown-compat-highlight-js.css?ver=1",
|
||||
|
5
src/Utils/Markdown/CommonMarkExtensions/ExtensionRef.php
Normal file
5
src/Utils/Markdown/CommonMarkExtensions/ExtensionRef.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
class ExtensionRef {
|
||||
|
||||
}
|
60
src/Utils/Markdown/Parser.php
Normal file
60
src/Utils/Markdown/Parser.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
use League\CommonMark\ConverterInterface;
|
||||
use League\CommonMark\Environment\Environment;
|
||||
use League\CommonMark\Extension\Attributes\AttributesExtension;
|
||||
use League\CommonMark\Extension\Autolink\AutolinkExtension;
|
||||
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||||
use League\CommonMark\Extension\DescriptionList\DescriptionListExtension;
|
||||
use League\CommonMark\Extension\Footnote\FootnoteExtension;
|
||||
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
|
||||
use League\CommonMark\Extension\Table\TableExtension;
|
||||
use League\CommonMark\Extension\TaskList\TaskListExtension;
|
||||
use League\CommonMark\MarkdownConverter;
|
||||
|
||||
class Parser {
|
||||
|
||||
private static ?ConverterInterface $converter = null;
|
||||
|
||||
public static function getDefaultParser (): ConverterInterface {
|
||||
|
||||
// MarkDown Parser:
|
||||
// CommonMark
|
||||
$parserEnv = new Environment();
|
||||
$parserEnv->addExtension(new CommonMarkCoreExtension());
|
||||
|
||||
// from GitHub Flavor Markdown
|
||||
// + autolink [https://link.to]
|
||||
// + strikethrough ~~removed~~
|
||||
// + table <table>
|
||||
// + task list [x]
|
||||
// - disallowed raw html <style><script>
|
||||
$parserEnv->addExtension(new AutolinkExtension());
|
||||
$parserEnv->addExtension(new StrikethroughExtension());
|
||||
$parserEnv->addExtension(new TableExtension());
|
||||
$parserEnv->addExtension(new TaskListExtension());
|
||||
|
||||
// from Kramdown (PHP Markdown Extra?)
|
||||
// + html attributes {#title-1}
|
||||
$parserEnv->addExtension(new AttributesExtension());
|
||||
// from PHP Markdown Extra
|
||||
// + footnote [^1]
|
||||
// + description list <dl><dt><dd>
|
||||
$parserEnv->addExtension(new FootnoteExtension());
|
||||
$parserEnv->addExtension(new DescriptionListExtension());
|
||||
|
||||
return new MarkdownConverter($parserEnv);
|
||||
|
||||
}
|
||||
|
||||
private static function getParser (): ConverterInterface {
|
||||
if (Parser::$converter === null)
|
||||
Parser::$converter = self::getDefaultParser();
|
||||
return Parser::$converter;
|
||||
}
|
||||
|
||||
public static function parse (string $article): string {
|
||||
return self::getParser()->convert($article);
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
<?php
|
||||
|
||||
require_once "./lib/Parsedown/Parsedown.php";
|
||||
require_once "./lib/Parsedown/ParsedownExtra.php";
|
||||
use Erusev\Parsedown\Parsedown;
|
||||
|
||||
class ParsedownExtend extends ParsedownExtra {
|
||||
class ParsedownExtend extends Parsedown {
|
||||
|
||||
function __construct() {
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
<?php
|
||||
|
||||
require_once "./src/Utils/ParsedownExtend.php";
|
||||
require_once "./src/Data/PageMeta.php";
|
||||
|
||||
$parser = new ParsedownExtend();
|
||||
|
||||
$parser->setMarkupEscaped(false);
|
||||
$parser->setSafeMode(false);
|
||||
require_once "./src/Utils/Markdown/Parser.php";
|
||||
|
||||
$pageMarkdownContent = PageMeta::$page->getMarkdownContent();
|
||||
|
||||
// if the `compatibility.article.title.oldversion` is enabled
|
||||
// that means the title should be auto-generated from book.xml
|
||||
// but not written on page.md.
|
||||
// this code will generate a title from book.xml if the start
|
||||
// of the page.md is not `# Title` formatting page title.
|
||||
if (PageMeta::compatibilityOldTitlePolicy()) {
|
||||
$length = strlen($pageMarkdownContent);
|
||||
for ($i=0; $i<$length; $i++) {
|
||||
@ -24,4 +24,4 @@ if (PageMeta::compatibilityOldTitlePolicy()) {
|
||||
}
|
||||
}
|
||||
|
||||
echo $parser->text($pageMarkdownContent);
|
||||
echo Parser::parse($pageMarkdownContent);
|
||||
|
Loading…
Reference in New Issue
Block a user