mirror of
https://github.com/YunoHost/doc.git
synced 2024-09-03 20:06:26 +02:00
342 lines
12 KiB
PHP
342 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* Presentation Plugin, Parser API
|
|
*
|
|
* PHP version 7
|
|
*
|
|
* @category API
|
|
* @package Grav\Plugin\PresentationPlugin
|
|
* @subpackage Grav\Plugin\PresentationPlugin\API
|
|
* @author Ole Vik <git@olevik.net>
|
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
* @link https://github.com/OleVik/grav-plugin-presentation
|
|
*/
|
|
|
|
namespace Grav\Plugin\PresentationPlugin\API;
|
|
|
|
use Grav\Common\Uri;
|
|
use Grav\Common\Utils;
|
|
use Grav\Plugin\PresentationPlugin\Utilities;
|
|
use Thunder\Shortcode\Parser\RegularParser;
|
|
use Thunder\Shortcode\Parser\RegexParser;
|
|
use Thunder\Shortcode\Parser\WordpressParser;
|
|
use Thunder\Shortcode\Processor\Processor;
|
|
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
|
use Thunder\Shortcode\HandlerContainer\HandlerContainer;
|
|
|
|
/**
|
|
* Parser API
|
|
*
|
|
* Parser API for parsing content
|
|
*
|
|
* @category Extensions
|
|
* @package Grav\Plugin\PresentationPlugin\API
|
|
* @author Ole Vik <git@olevik.net>
|
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
* @link https://github.com/OleVik/grav-plugin-presentation
|
|
*/
|
|
class Parser implements ParserInterface
|
|
{
|
|
/**
|
|
* Regular expressions
|
|
*/
|
|
const REGEX_MEDIA_P = '/<p>\s*(<a .*>\s*<img.*\s*<\/a>|\s*<img.*|<img.*\s*|<video.*|<audio.*)\s*<\/p>/i';
|
|
|
|
/**
|
|
* Instantiate Parser API
|
|
*
|
|
* @param array $config Plugin configuration
|
|
* @param Transport $transport Transport API
|
|
*/
|
|
public function __construct($config, $transport)
|
|
{
|
|
$this->config = $config;
|
|
$this->transport = $transport;
|
|
// @deprecated 2.0.0
|
|
$this->props = [];
|
|
}
|
|
|
|
/**
|
|
* Parse shortcodes
|
|
*
|
|
* @param string $content Markdown content in Page
|
|
* @param string $id Slide ID
|
|
* @param array $page Page configuration
|
|
*
|
|
* @return array Processed content and shortcodes
|
|
*/
|
|
public function processShortcodes(string $content, string $id, array $page)
|
|
{
|
|
$handlers = new HandlerContainer();
|
|
$path = $page['path'];
|
|
$handlers->setDefault(
|
|
function (ShortcodeInterface $sc) use ($id, $path) {
|
|
$name = $sc->getName();
|
|
$value = $sc->getParameter($name, $sc->getBbCode()) ?? '';
|
|
if (Utils::startsWith($name, 'class')) {
|
|
$this->transport->setClass($id, $value);
|
|
} elseif (Utils::startsWith($name, 'style')) {
|
|
$property = str_replace('style-', '', $name);
|
|
$this->styleProcessor($id, $property, $value, [$path]);
|
|
} elseif (Utils::startsWith($name, 'styles')) {
|
|
$property = str_replace('styles-', '', $name);
|
|
$this->styleProcessor($id, $property, $value, [$path]);
|
|
} elseif (Utils::startsWith($name, 'data')) {
|
|
$property = str_replace('data-', '', $name);
|
|
$this->transport->setDataAttribute($id, $property, $value);
|
|
} elseif (Utils::startsWith($name, 'hide')) {
|
|
$this->transport->setDataAttribute($id, 'hide', "true");
|
|
}
|
|
return;
|
|
}
|
|
);
|
|
$parser = "Thunder\Shortcode\Parser\\" . $this->config['shortcode_parser'];
|
|
$parser = new $parser();
|
|
$processor = new Processor($parser, $handlers);
|
|
return [
|
|
'content' => $processor->process($content),
|
|
'shortcodes' => $parser->parse($content)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Process key-value pairs of options
|
|
*
|
|
* @param array $data Key-value pairs of options
|
|
* @param string $id Target id-attribute
|
|
* @param array $paths Paths to use for file-finding
|
|
* @param string $mode Which Transport to perform
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function processor(array $data, string $id, array $paths = [], string $mode = 'style')
|
|
{
|
|
if (empty($data)) {
|
|
return false;
|
|
}
|
|
foreach ($data as $key => $value) {
|
|
if (Utils::startsWith('style-', $key)) {
|
|
$mode = 'style';
|
|
} elseif (Utils::startsWith('styles-', $key)) {
|
|
$mode = 'style';
|
|
} elseif (Utils::startsWith('data-', $key)) {
|
|
$mode = 'data';
|
|
} elseif (Utils::startsWith('aria-', $key)) {
|
|
$mode = 'aria';
|
|
}
|
|
$key = str_replace($mode . '-', '', $key);
|
|
if ($mode == 'style') {
|
|
if (empty($value)) {
|
|
continue;
|
|
}
|
|
$this->styleProcessor($id, $key, $value, $paths);
|
|
} elseif ($mode == 'data') {
|
|
$this->transport->setDataAttribute($id, $key, $value);
|
|
} elseif ($mode == 'aria') {
|
|
$this->transport->setAriaAttribute($id, $key, $value);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Process style
|
|
*
|
|
* @param string $id Target id-attribute
|
|
* @param string $property CSS property name
|
|
* @param string $value CSS property value
|
|
* @param array $paths Locations to search for asset in
|
|
*
|
|
* @return void
|
|
*/
|
|
public function styleProcessor(string $id, string $property, string $value, array $paths = [])
|
|
{
|
|
if ($property == 'background-image') {
|
|
if (!Uri::isValidUrl($value)) {
|
|
$locations = array_merge(
|
|
$paths,
|
|
[
|
|
'',
|
|
'user/pages',
|
|
'user/pages/images',
|
|
]
|
|
);
|
|
$locations = Utilities::explodeFileLocations($locations, GRAV_ROOT, '/', '/');
|
|
$file = Utilities::fileFinder($value, $locations);
|
|
$file = str_ireplace(GRAV_ROOT, '', $file);
|
|
$value = $file;
|
|
}
|
|
$this->transport->setStyle($id, "{\n$property: url($value);\n}");
|
|
} elseif ($property == 'header-font-family') {
|
|
$this->transport->setStyle($id, "{\nfont-family:$value;\n}", 'h1,h2,h3,h4,h5,h6');
|
|
} elseif ($property == 'header-color') {
|
|
$this->transport->setStyle($id, "{\ncolor:$value;\n}", 'h1,h2,h3,h4,h5,h6');
|
|
} elseif ($property == 'block-font-family') {
|
|
$this->transport->setStyle($id, "{\nfont-family:$value;\n}");
|
|
} elseif ($property == 'block-color') {
|
|
$this->transport->setStyle($id, "{\ncolor:$value;\n}");
|
|
} else {
|
|
$this->transport->setStyle($id, "{\n$property:$value;\n}");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set modular scales in CSS
|
|
*
|
|
* @param string $id Slide id-attribute
|
|
* @param string $scale Modular Scale Ratio
|
|
* @param float $modifier Optional multiplication-parameter
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setModularScale(string $id, string $scale, float $modifier = null)
|
|
{
|
|
$scale = (float) $scale;
|
|
$steps = array(6, 5, 4, 3, 2, 1, 0);
|
|
for ($i = 1; $i <= 6; $i++) {
|
|
$value = self::modularScale($steps[$i], 16, $scale, true);
|
|
$value = $modifier != null ? $value * $modifier : $value;
|
|
$this->transport->setStyle($id, '{font-size:' . $value . 'em;}', 'h' . $i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get font-size in pixels
|
|
*
|
|
* @param integer $step Step in scale
|
|
* @param integer $base Base font-size
|
|
* @param float $ratio Rhythm
|
|
* @param bool $relative Output relative units
|
|
*
|
|
* @return float Modular Scale breakpoint
|
|
*/
|
|
public static function modularScale(int $step, int $base, float $ratio, bool $relative = null)
|
|
{
|
|
if ($relative == true) {
|
|
return round((pow($ratio, $step) * $base) / $base, 3);
|
|
} else {
|
|
return round((pow($ratio, $step) * $base), 2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove wrapping paragraph from img-element
|
|
*
|
|
* @param string $content Markdown content in Page
|
|
*
|
|
* @return string Processed content
|
|
*/
|
|
public static function unwrapImage(string $content)
|
|
{
|
|
$unwrap = self::REGEX_MEDIA_P;
|
|
$content = preg_replace($unwrap, "$1", $content);
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Parse shortcodes
|
|
*
|
|
* @param string $content Markdown content in Page
|
|
* @param string $id Slide id-attribute
|
|
*
|
|
* @deprecated 2.0.0
|
|
*
|
|
* @return array Processed contents and properties
|
|
*/
|
|
public function interpretShortcodes(string $content, string $id)
|
|
{
|
|
$handlers = new HandlerContainer();
|
|
$handlers->setDefault(
|
|
function (ShortcodeInterface $sc) {
|
|
$return = array();
|
|
$name = $sc->getName();
|
|
$value = $sc->getParameter($name, $sc->getBbCode());
|
|
if (Utils::startsWith($name, 'class')) {
|
|
$this->props['class'] = $value;
|
|
} elseif (Utils::startsWith($name, 'style')) {
|
|
$name = str_replace('style-', '', $name);
|
|
$this->props['styles'][$name] = $value;
|
|
} elseif (Utils::startsWith($name, 'data')) {
|
|
$this->props['styles'][$name] = $value;
|
|
} elseif (Utils::startsWith($name, 'hide')) {
|
|
$this->props['hide'] = [true];
|
|
}
|
|
return;
|
|
}
|
|
);
|
|
$parser = "Thunder\Shortcode\Parser\\" . $this->config['shortcode_parser'];
|
|
$processor = new Processor(new $parser(), $handlers);
|
|
return ['content' => $processor->process($content), 'props' => $this->props];
|
|
}
|
|
|
|
/**
|
|
* Create HTML for fragments
|
|
*
|
|
* @param string $content Markdown content in Page
|
|
*
|
|
* @deprecated 2.0.0
|
|
*
|
|
* @return string Processed contents
|
|
*/
|
|
public function processFragments(string $content)
|
|
{
|
|
$content = preg_replace(
|
|
self::REGEX_FRAGMENT_SHORTCODE,
|
|
'<span class="fragment \\1">\\2</span>',
|
|
$content
|
|
);
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Process styles and data-attributes
|
|
*
|
|
* @param array $styles List of key-value pairs
|
|
* @param string $route Route to Page for relative assets
|
|
* @param string $id Slide id-attribute
|
|
* @param string $base Base path to prepend to file
|
|
*
|
|
* @deprecated 2.0.0
|
|
*
|
|
* @return string Processed styles, in inline string
|
|
*/
|
|
public function processStylesData(array $styles, string $route, string $id, string $base = "")
|
|
{
|
|
$inline = $data = '';
|
|
foreach ($styles as $property => $value) {
|
|
if ($property == 'background-image') {
|
|
if (!Uri::isValidUrl($value)) {
|
|
$locations = array(
|
|
'',
|
|
'user/pages',
|
|
'user/pages/images',
|
|
);
|
|
$locations = Utilities::explodeFileLocations($locations, GRAV_ROOT, '/', '/');
|
|
$file = Utilities::fileFinder($value, $locations);
|
|
$file = str_ireplace(GRAV_ROOT, '', $file);
|
|
$value = $base . $file;
|
|
}
|
|
$inline .= $property . ': url(' . $value . ');';
|
|
} elseif (Utils::startsWith($property, 'data')) {
|
|
$data .= ' ' . $property . '="' . $value . '"';
|
|
if ($property == 'data-textsize-scale') {
|
|
$this->transport->setClass($id, 'textsizing');
|
|
}
|
|
} elseif ($property == 'header-font-family') {
|
|
$this->transport->setStyle($id, "{\nfont-family:$value;\n}", 'h1,h2,h3,h4,h5,h6');
|
|
} elseif ($property == 'header-color') {
|
|
$this->transport->setStyle($id, "{\ncolor:$value;\n}", 'h1,h2,h3,h4,h5,h6');
|
|
} elseif ($property == 'block-font-family') {
|
|
$this->transport->setStyle($id, "{\nfont-family:$value;\n}");
|
|
} elseif ($property == 'block-color') {
|
|
$this->transport->setStyle($id, "{\ncolor:$value;\n}");
|
|
} else {
|
|
$inline .= $property . ': ' . $value . ';';
|
|
}
|
|
}
|
|
return array(
|
|
'style' => $inline,
|
|
'data' => $data
|
|
);
|
|
}
|
|
}
|