From bf0b9167219314393bdb577efcee7fa6b0f54d0d Mon Sep 17 00:00:00 2001 From: Andrey Solovov <pan.russian@gmail.com> Date: Sat, 16 Jan 2021 18:48:17 +0300 Subject: [PATCH 1/4] Footnotes implementation --- MarkdownExtra.php | 4 +- block/FooterTrait.php | 156 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 block/FooterTrait.php diff --git a/MarkdownExtra.php b/MarkdownExtra.php index eedcf77..c20614e 100644 --- a/MarkdownExtra.php +++ b/MarkdownExtra.php @@ -2,6 +2,7 @@ namespace cebe\markdown; +use cebe\markdown\block\FooterTrait; use cebe\markdown\block\TableTrait; // work around https://github.com/facebook/hhvm/issues/1120 @@ -19,6 +20,7 @@ class MarkdownExtra extends Markdown // include block element parsing using traits use block\TableTrait; use block\FencedCodeTrait; + use block\FooterTrait; // include inline element parsing using traits // TODO @@ -60,8 +62,6 @@ class MarkdownExtra extends Markdown // TODO implement definition lists - // TODO implement footnotes - // TODO implement Abbreviations diff --git a/block/FooterTrait.php b/block/FooterTrait.php new file mode 100644 index 0000000..8c34c9a --- /dev/null +++ b/block/FooterTrait.php @@ -0,0 +1,156 @@ +<?php +/** + * Created by Andrew Solovov. + * User: Andrey + * Date: 15.01.2021 + * Time: 18:36 + * + * Project name: markdown + * + * @author Andrew Solovov <pan.russian@gmail.com> + * @copyright 1997-2021 Pan Russian solovov.ru + */ + +namespace cebe\markdown\block; + + +trait FooterTrait +{ + + protected $footnotes = []; + protected $footnoteNum = 1; + + /** + * @param $text + * @return string + */ + public function parse($text) + { + $absy = $this->parseBlocks(explode("\n", $text)); + + foreach ($absy as $block) { + if ($block[0] == 'footnote') { + $block['num'] = $this->footnoteNum; + $this->footnotes[] = $block; + $this->footnoteNum++; + } + } + $markup = parent::parse($text); + $markup = $this->applyFooter($markup, $this->footnotes); + + return $markup; + } + + /** + * @param $content + * @param $blocks + * @return string + */ + protected function applyFooter($content, $blocks) + { + $content .= '<hr>'; + foreach ($blocks as $block) { + $number = $block['num'] . ". "; + $link = '<a href="#fnref:' . $block['id'] . '" class="footnote-backref">↩</a>'; + $text = $this->renderAbsy($block['content']); + $text = substr_replace($text, $number, 3, 0); + $text = substr_replace($text, $link, -5, 0); + + $content .= Html::tag('footnotes', $text, [ + 'id' => 'fn:'.$block['id'] + ]) . "\n"; + } + return $content; + } + + /** + * Parses a footnote link indicated by `[^`. + * @marker [^ + * @param $text + * @return array + */ + protected function parseFootnoteLink($text) + { + if (preg_match('/^\[\^(.+?)\]/', $text, $matches)) { + return [ + ['footnoteLink', $matches[1]], + strlen($matches[0]) + ]; + } + return [['text', $text[0]], 1]; + } + + /** + * @param $block + * @return string + */ + protected function renderFootnoteLink($block) + { + $footnoteId = $block[1]; + $num = 0; + $found = false; + foreach ($this->footnotes as $footnote) { + $num ++; + if ($footnote['id']==$footnoteId) { + $found = true; + break; + } + } + if (!$found) + $num = '?'; + + $text = htmlspecialchars($block[1], ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8'); + return '<sup id="fnref:' . $text . '"><a href="#fn:' . $text . '" class="footnote-ref" rel="footnote">[' . $num . ']</a></sup>'; + } + + /** + * identify a line as the beginning of a footnote block + * + * @param $line + * @return false|int + */ + protected function identifyFootnoteList($line) + { + return preg_match('/^\[\^(.+?)\]:/', $line); + } + + /** + * Consume lines for a footnote + */ + protected function consumeFootnoteList($lines, $current) + { + $id = ''; + $content = []; + $count = count($lines); + for ($i = $current; $i < $count; $i++) { + $line = $lines[$i]; + + if ($id == '') { + if (preg_match('/^\[\^(.+?)\]:[ \t]+/', $line, $matches)) { + $id = $matches[1]; + $str = substr($line, strlen($matches[0])); + $content[] = $str; + } + } else if (strlen(trim($line)) == 0) { + break; + } else { + $content[] = ltrim($line); + } + } + + $block = ['footnote', 'id' => $id, 'content' => $this->parseBlocks($content)]; + + return [$block, $i]; + } + + /** + * @param $block + * @return string + */ + protected function renderFootnote($block) + { + return ''; + } + + +} \ No newline at end of file From 028229f7a3bc146efe186d184ee74dbe1bb9e129 Mon Sep 17 00:00:00 2001 From: Andrey Solovov <pan.russian@gmail.com> Date: Sat, 16 Jan 2021 18:53:48 +0300 Subject: [PATCH 2/4] small fix --- block/FooterTrait.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/block/FooterTrait.php b/block/FooterTrait.php index 8c34c9a..0581513 100644 --- a/block/FooterTrait.php +++ b/block/FooterTrait.php @@ -56,9 +56,7 @@ protected function applyFooter($content, $blocks) $text = substr_replace($text, $number, 3, 0); $text = substr_replace($text, $link, -5, 0); - $content .= Html::tag('footnotes', $text, [ - 'id' => 'fn:'.$block['id'] - ]) . "\n"; + $content .= '<footnotes id="fn:' . $block['id'] . '">' . $text . "</footnotes>\n"; } return $content; } From e602cef97872eec82b46617757e3f093478a1842 Mon Sep 17 00:00:00 2001 From: Andrew Solovov <pan.russian@gmail.com> Date: Wed, 20 Jan 2021 20:40:28 +0300 Subject: [PATCH 3/4] footnote with link --- block/FooterTrait.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/FooterTrait.php b/block/FooterTrait.php index 0581513..c01af8c 100644 --- a/block/FooterTrait.php +++ b/block/FooterTrait.php @@ -51,8 +51,9 @@ protected function applyFooter($content, $blocks) $content .= '<hr>'; foreach ($blocks as $block) { $number = $block['num'] . ". "; - $link = '<a href="#fnref:' . $block['id'] . '" class="footnote-backref">↩</a>'; + $link = '<a href="#fnref:' . $block['id'] . '" class="footnote-backref">↑</a>'; $text = $this->renderAbsy($block['content']); + $text = $this->renderAbsy($this->parseInline($text)); $text = substr_replace($text, $number, 3, 0); $text = substr_replace($text, $link, -5, 0); From a893b9672ade285ad39c9cc04f6dd5b707ae598c Mon Sep 17 00:00:00 2001 From: Andrew Solovov <pan.russian@gmail.com> Date: Wed, 20 Jan 2021 22:27:54 +0300 Subject: [PATCH 4/4] Add tests --- block/FooterTrait.php | 1 - tests/extra-data/footernote.md | 3 +++ tests/extra-data/foternote.html | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/extra-data/footernote.md create mode 100644 tests/extra-data/foternote.html diff --git a/block/FooterTrait.php b/block/FooterTrait.php index c01af8c..d215284 100644 --- a/block/FooterTrait.php +++ b/block/FooterTrait.php @@ -48,7 +48,6 @@ public function parse($text) */ protected function applyFooter($content, $blocks) { - $content .= '<hr>'; foreach ($blocks as $block) { $number = $block['num'] . ". "; $link = '<a href="#fnref:' . $block['id'] . '" class="footnote-backref">↑</a>'; diff --git a/tests/extra-data/footernote.md b/tests/extra-data/footernote.md new file mode 100644 index 0000000..cd47d7f --- /dev/null +++ b/tests/extra-data/footernote.md @@ -0,0 +1,3 @@ +The implementation focus is to be fast (see benchmark) and extensible [^1] + +[^1]: See [benchmark](https://github.com/kzykhys/Markbench#readme). \ No newline at end of file diff --git a/tests/extra-data/foternote.html b/tests/extra-data/foternote.html new file mode 100644 index 0000000..85d2a0c --- /dev/null +++ b/tests/extra-data/foternote.html @@ -0,0 +1,6 @@ +The implementation focus is to be fast (see benchmark) and extensible <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" rel="footnote">[1]</a></sup> + +<hr> +<footnotes id="fn:1"><p>1. See <a href="https://github.com/kzykhys/Markbench#readme">benchmark</a>. + <a href="#fnref:1" class="footnote-backref">↑</a></p> +</footnotes> \ No newline at end of file