2021-04-20 20:50:33 +02:00
|
|
|
|
<?php
|
|
|
|
|
namespace Grav\Plugin;
|
|
|
|
|
|
|
|
|
|
use Grav\Common\Plugin;
|
|
|
|
|
use RocketTheme\Toolbox\Event\Event;
|
|
|
|
|
|
|
|
|
|
class MarkdownTypographyPlugin extends Plugin
|
|
|
|
|
{
|
|
|
|
|
private array $thin_arrows;
|
|
|
|
|
private array $thick_arrows;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function getSubscribedEvents()
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
"onMarkdownInitialized" => ["onMarkdownInitialized", 0],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function onMarkdownInitialized(Event $event)
|
|
|
|
|
{
|
|
|
|
|
$markdown = $event["markdown"];
|
|
|
|
|
$config = $this->grav["config"]->get("plugins.".$this->name);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Once Grav requires Parsedown 1.8:
|
|
|
|
|
// - $Element["name"] is no longer required, so remove redundant spans
|
|
|
|
|
// - $Element["handler"] changes for extents with Markdown inside
|
|
|
|
|
// - Do arrows starting with "<" work in that version?
|
|
|
|
|
|
|
|
|
|
$arrows = $config["arrows"];
|
|
|
|
|
|
|
|
|
|
// Enable arrows (before dashes to avoid conflict)
|
|
|
|
|
if ($arrows["enabled"]) {
|
|
|
|
|
$this->thin_arrows = explode(",", $arrows["thin_arrows"]) ?: ["→","←","⟶","⟵"];
|
|
|
|
|
$this->thick_arrows = explode(",", $arrows["thick_arrows"]) ?: ["⇒","⇐","⟹","⟸"];
|
|
|
|
|
|
|
|
|
|
// Add inline types
|
|
|
|
|
$markdown->addInlineType("-", "TypographyArrowSingleRight");
|
|
|
|
|
$markdown->addInlineType("=", "TypographyArrowDoubleRight");
|
|
|
|
|
//$markdown->addInlineType(">", "TypographyArrowBothLeft");
|
|
|
|
|
|
|
|
|
|
// Add function to handle inline type
|
|
|
|
|
$markdown->inlineTypographyArrowSingleRight = function($excerpt) {
|
|
|
|
|
if (preg_match("/^(--?>)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[1]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => strlen($matches[1]) == 2 ? $this->thin_arrows[0] : $this->thin_arrows[2 % count($this->thin_arrows)], // single right arrow (long arrow: ⟶)
|
|
|
|
|
"attributes" => [
|
|
|
|
|
"class" => "typography-arrow"
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elseif (preg_match("/^(--?<)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[1]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => strlen($matches[1]) == 2 ? $this->thin_arrows[1] : $this->thin_arrows[3 % count($this->thin_arrows)], // single left arrow (long arrow: ⟵)
|
|
|
|
|
"attributes" => [
|
|
|
|
|
"class" => "typography-arrow"
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$markdown->inlineTypographyArrowDoubleRight = function($excerpt) {
|
|
|
|
|
if (preg_match("/^(==?>)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[1]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => strlen($matches[1]) == 2 ? $this->thick_arrows[0] : $this->thick_arrows[2 % count($this->thick_arrows)], // fat right arrow
|
|
|
|
|
"attributes" => [
|
|
|
|
|
"class" => "typography-arrow"
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elseif (preg_match("/^(==?<)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[1]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => strlen($matches[1]) == 2 ? $this->thick_arrows[1] : $this->thick_arrows[3 % count($this->thick_arrows)], // fat left arrow
|
|
|
|
|
"attributes" => [
|
|
|
|
|
"class" => "typography-arrow"
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// TODO: Doesn't seem to work at the moment!?
|
|
|
|
|
/*$markdown->inlineTypographyArrowBothLeft = function($excerpt) {
|
|
|
|
|
$this->grav["debugger"]->addMessage($excerpt);
|
|
|
|
|
if (preg_match("/^(.--?)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[1]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "←", // thin left arrow
|
|
|
|
|
"attributes" => [
|
|
|
|
|
"class" => "typography-arrow"
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elseif (preg_match("/^(.==?)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[1]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "⇦", // fat left arrow
|
|
|
|
|
"attributes" => [
|
|
|
|
|
"class" => "typography-arrow"
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable dashes
|
|
|
|
|
if ($config["dashes"]) {
|
|
|
|
|
// Add inline type
|
|
|
|
|
$markdown->addInlineType("-", "TypographyDash");
|
|
|
|
|
|
|
|
|
|
// Add function to handle inline type
|
|
|
|
|
$markdown->inlineTypographyDash = function($excerpt) {
|
|
|
|
|
if (preg_match("/-/", $excerpt["text"], $matches) && preg_match("/ (-) /", $excerpt["context"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 1,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "–", // en dash
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} elseif (preg_match("/^(---)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 3,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "—", // em dash
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} elseif (preg_match("/^(--)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 2,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "–", // en dash
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable smart-quotes
|
|
|
|
|
if ($config["smart_quotes"]) {
|
|
|
|
|
// Add inline types
|
|
|
|
|
$markdown->addInlineType('"', "TypographyDoubleQuote");
|
|
|
|
|
$markdown->addInlineType("'", "TypographySingleQuote");
|
|
|
|
|
|
|
|
|
|
// Add function to handle double quotes
|
|
|
|
|
$markdown->inlineTypographyDoubleQuote = function($excerpt) {
|
|
|
|
|
if (preg_match_all('/^"([\s\S]*?)"/', $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[0][0]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "“".$matches[1][0]."”", // double-quotes
|
|
|
|
|
"handler" => "line",
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elseif (preg_match('/^\s?"/', $excerpt["context"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($excerpt["text"]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "“" . substr($excerpt["text"], 1), // Lonely opening double-quotes
|
|
|
|
|
"handler" => "line",
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Add function to handle single quotes (+ apostrophes)
|
|
|
|
|
$markdown->inlineTypographySingleQuote = function($excerpt) {
|
|
|
|
|
if (preg_match("/\w".preg_quote($excerpt["text"], "/")."/", $excerpt["context"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 1,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "’", // apostrophe
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elseif (preg_match_all("/^'([\s\S]*?)'(?=\W?)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[0][0]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "‘".$matches[1][0]."’", // single-quotes
|
|
|
|
|
"handler" => "line",
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elseif (preg_match("/^(')|(\s')/", $excerpt["context"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($excerpt["text"]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "‘" . substr($excerpt["text"], 1), // Lonely opening single-quote
|
|
|
|
|
"handler" => "line",
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Don't process inside of inline shortcode tags
|
|
|
|
|
// This is a fix to stop quoted attributes getting broken when inline
|
|
|
|
|
// Temporary tags are stripped by $this->stripTemporaryTags()
|
|
|
|
|
// Once Parsedown 1.8 is used in Grav, the temp tags will be unnecessary
|
|
|
|
|
$markdown->addInlineType("[", "TypographyShortcode");
|
|
|
|
|
$markdown->inlineTypographyShortcode = function($excerpt) {
|
2021-10-17 18:09:53 +02:00
|
|
|
|
if (preg_match("/\[[\s\S]+?((\".+?\")|(\'.+?\')|(\[.+?\])|([\s\S]+?))*?\]/", $excerpt["text"], $matches))
|
2021-04-20 20:50:33 +02:00
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[0]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "temporary-tag",
|
|
|
|
|
"text" => $matches[0],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$this->enable(["onOutputGenerated" => ["stripTemporaryTags", 0]]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable ellipsis
|
|
|
|
|
if ($config["ellipsis"]) {
|
|
|
|
|
// Add inline type
|
|
|
|
|
$markdown->addInlineType(".", "TypographyEllipsis");
|
|
|
|
|
|
|
|
|
|
// Add function to handle inline type
|
|
|
|
|
$markdown->inlineTypographyEllipsis = function($excerpt) {
|
|
|
|
|
if (preg_match("/^(\.\.\.)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 3,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "…", // ellipsis
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable line breaks
|
|
|
|
|
if ($config["line_breaks"]) {
|
|
|
|
|
// Add inline type
|
|
|
|
|
$markdown->addInlineType("\\", "TypographyLineBreaks");
|
|
|
|
|
|
|
|
|
|
// Add function to handle inline type
|
|
|
|
|
$markdown->inlineTypographyLineBreaks = function($excerpt) {
|
|
|
|
|
if (preg_match("/\\\n/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 1,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "br", // line break
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable interrobang
|
|
|
|
|
if ($config["interrobang"]) {
|
|
|
|
|
// Add inline type
|
|
|
|
|
$markdown->addInlineType("!", "TypographyInterrobang");
|
|
|
|
|
|
|
|
|
|
// Add function to handle inline type
|
|
|
|
|
$markdown->inlineTypographyInterrobang = function($excerpt) {
|
|
|
|
|
if (preg_match("/^(!\?)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 2,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "‽", // interrobang
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable plus-minus
|
|
|
|
|
if ($config["plus_minus"]) {
|
|
|
|
|
// Add inline type
|
|
|
|
|
$markdown->addInlineType("+", "TypographyPlusMinus");
|
|
|
|
|
|
|
|
|
|
// Add function to handle inline type
|
|
|
|
|
$markdown->inlineTypographyPlusMinus = function($excerpt) {
|
|
|
|
|
if (preg_match("/^(\+\/?-)/", $excerpt["text"], $matches))
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => strlen($matches[1]),
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "±", // plus-minus
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable X Times
|
|
|
|
|
if ($config["x_times"]) {
|
|
|
|
|
// Add inline type
|
|
|
|
|
$markdown->addInlineType("x", "TypographyXTimes");
|
|
|
|
|
|
|
|
|
|
// Add function to handle inline type
|
|
|
|
|
$markdown->inlineTypographyXTimes = function($excerpt) {
|
|
|
|
|
if (
|
|
|
|
|
(substr($excerpt["text"], 0, 2) == "x ") and
|
2021-10-17 18:09:53 +02:00
|
|
|
|
(preg_match("/( ".preg_quote($excerpt["text"], "/").")/", $excerpt["context"], $matches))
|
2021-04-20 20:50:33 +02:00
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
return array(
|
|
|
|
|
"extent" => 1,
|
|
|
|
|
"element" => array(
|
|
|
|
|
"name" => "span",
|
|
|
|
|
"text" => "×", // times symbol
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function stripTemporaryTags()
|
|
|
|
|
{
|
|
|
|
|
// Check that it's an HTML page, abort if not
|
|
|
|
|
if ($this->grav["page"]->templateFormat() !== 'html') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the rendered content (HTML)
|
|
|
|
|
$content = $this->grav->output;
|
|
|
|
|
|
|
|
|
|
// Remove the temporary tags
|
|
|
|
|
$content = str_replace("<temporary-tag>", "", $content);
|
|
|
|
|
$content = str_replace("</temporary-tag>", "", $content);
|
|
|
|
|
|
|
|
|
|
// Remove encoded temporary tags (eg. those generatred by [safe-email])
|
|
|
|
|
$content = str_replace("<temporary-tag>", "", $content);
|
|
|
|
|
$content = str_replace("</temporary-tag>", "", $content);
|
|
|
|
|
|
|
|
|
|
// Rewrite the output
|
|
|
|
|
$this->grav->output = $content;
|
|
|
|
|
}
|
|
|
|
|
}
|