Initial commit
This commit is contained in:
commit
73c6b816c0
716 changed files with 170045 additions and 0 deletions
211
kirby/src/Text/KirbyTag.php
Normal file
211
kirby/src/Text/KirbyTag.php
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Text;
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Exception\BadMethodCallException;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Representation and parse of a single KirbyTag.
|
||||
*
|
||||
* @package Kirby Text
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class KirbyTag
|
||||
{
|
||||
public static $aliases = [];
|
||||
public static $types = [];
|
||||
|
||||
public $attrs = [];
|
||||
public $data = [];
|
||||
public $options = [];
|
||||
public $type = null;
|
||||
public $value = null;
|
||||
|
||||
public function __call(string $name, array $arguments = [])
|
||||
{
|
||||
return $this->data[$name] ?? $this->$name;
|
||||
}
|
||||
|
||||
public static function __callStatic(string $type, array $arguments = [])
|
||||
{
|
||||
return (new static($type, ...$arguments))->render();
|
||||
}
|
||||
|
||||
public function __construct(string $type, string $value = null, array $attrs = [], array $data = [], array $options = [])
|
||||
{
|
||||
if (isset(static::$types[$type]) === false) {
|
||||
if (isset(static::$aliases[$type]) === false) {
|
||||
throw new InvalidArgumentException('Undefined tag type: ' . $type);
|
||||
}
|
||||
|
||||
$type = static::$aliases[$type];
|
||||
}
|
||||
|
||||
$kirby = $data['kirby'] ?? App::instance();
|
||||
$defaults = $kirby->option('kirbytext.' . $type, []);
|
||||
$attrs = array_replace($defaults, $attrs);
|
||||
|
||||
// all available tag attributes
|
||||
$availableAttrs = static::$types[$type]['attr'] ?? [];
|
||||
|
||||
foreach ($attrs as $attrName => $attrValue) {
|
||||
$attrName = strtolower($attrName);
|
||||
|
||||
// applies only defined attributes to safely update
|
||||
if (in_array($attrName, $availableAttrs) === true) {
|
||||
$this->{$attrName} = $attrValue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->attrs = $attrs;
|
||||
$this->data = $data;
|
||||
$this->options = $options;
|
||||
$this->$type = $value;
|
||||
$this->type = $type;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function __get(string $attr)
|
||||
{
|
||||
$attr = strtolower($attr);
|
||||
return $this->$attr ?? null;
|
||||
}
|
||||
|
||||
public function attr(string $name, $default = null)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
return $this->$name ?? $default;
|
||||
}
|
||||
|
||||
public static function factory(...$arguments)
|
||||
{
|
||||
return (new static(...$arguments))->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a file for the given path.
|
||||
* The method first searches the file
|
||||
* in the current parent, if it's a page.
|
||||
* Afterwards it uses Kirby's global file finder.
|
||||
*
|
||||
* @param string $path
|
||||
* @return \Kirby\Cms\File|null
|
||||
*/
|
||||
public function file(string $path)
|
||||
{
|
||||
$parent = $this->parent();
|
||||
|
||||
if (
|
||||
is_object($parent) === true &&
|
||||
method_exists($parent, 'file') === true &&
|
||||
$file = $parent->file($path)
|
||||
) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
if (
|
||||
is_a($parent, 'Kirby\Cms\File') === true &&
|
||||
$file = $parent->page()->file($path)
|
||||
) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return $this->kirby()->file($path, null, true);
|
||||
}
|
||||
/**
|
||||
* Returns the current Kirby instance
|
||||
*
|
||||
* @return \Kirby\Cms\App
|
||||
*/
|
||||
public function kirby()
|
||||
{
|
||||
return $this->data['kirby'] ?? App::instance();
|
||||
}
|
||||
|
||||
public function option(string $key, $default = null)
|
||||
{
|
||||
return $this->options[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @param array $data
|
||||
* @param array $options
|
||||
* @return static
|
||||
*/
|
||||
public static function parse(string $string, array $data = [], array $options = [])
|
||||
{
|
||||
// remove the brackets, extract the first attribute (the tag type)
|
||||
$tag = trim(ltrim($string, '('));
|
||||
|
||||
// use substr instead of rtrim to keep non-tagged brackets
|
||||
// (link: file.pdf text: Download (PDF))
|
||||
if (substr($tag, -1) === ')') {
|
||||
$tag = substr($tag, 0, -1);
|
||||
}
|
||||
|
||||
$type = trim(substr($tag, 0, strpos($tag, ':')));
|
||||
$type = strtolower($type);
|
||||
$attr = static::$types[$type]['attr'] ?? [];
|
||||
|
||||
// the type should be parsed as an attribute, so we add it here
|
||||
// to the list of possible attributes
|
||||
array_unshift($attr, $type);
|
||||
|
||||
// extract all attributes
|
||||
$regex = sprintf('/(%s):/i', implode('|', $attr));
|
||||
$search = preg_split($regex, $tag, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
// $search is now an array with alternating keys and values
|
||||
// convert it to arrays of keys and values
|
||||
$chunks = array_chunk($search, 2);
|
||||
$keys = array_column($chunks, 0);
|
||||
$values = array_map('trim', array_column($chunks, 1));
|
||||
|
||||
// ensure that there is a value for each key
|
||||
// otherwise combining won't work
|
||||
if (count($values) < count($keys)) {
|
||||
$values[] = '';
|
||||
}
|
||||
|
||||
// combine the two arrays to an associative array
|
||||
$attributes = array_combine($keys, $values);
|
||||
|
||||
// the first attribute is the type attribute
|
||||
// extract and pass its value separately
|
||||
$value = array_shift($attributes);
|
||||
|
||||
return new static($type, $value, $attributes, $data, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent model
|
||||
*
|
||||
* @return \Kirby\Cms\Model|null
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->data['parent'];
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$callback = static::$types[$this->type]['html'] ?? null;
|
||||
|
||||
if (is_a($callback, 'Closure') === true) {
|
||||
return (string)$callback($this);
|
||||
}
|
||||
|
||||
throw new BadMethodCallException('Invalid tag render function in tag: ' . $this->type);
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
54
kirby/src/Text/KirbyTags.php
Normal file
54
kirby/src/Text/KirbyTags.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Text;
|
||||
|
||||
use Exception;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
/**
|
||||
* Parses and converts custom kirbytags in any
|
||||
* given string. KirbyTags are defined via
|
||||
* `KirbyTag::$types`. The default tags for the
|
||||
* Cms are located in `kirby/config/tags.php`
|
||||
*
|
||||
* @package Kirby Text
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class KirbyTags
|
||||
{
|
||||
public static function parse(string $text = null, array $data = [], array $options = []): string
|
||||
{
|
||||
$regex = '!
|
||||
(?=[^\]]) # positive lookahead that matches a group after the main expression without including ] in the result
|
||||
(?=\([a-z0-9_-]+:) # positive lookahead that requires starts with ( and lowercase ASCII letters, digits, underscores or hyphens followed with : immediately to the right of the current location
|
||||
(\( # capturing group 1
|
||||
(?:[^()]+|(?1))*+ # repetitions of any chars other than ( and ) or the whole group 1 pattern (recursed)
|
||||
\)) # end of capturing group 1
|
||||
!isx';
|
||||
|
||||
return preg_replace_callback($regex, function ($match) use ($data, $options) {
|
||||
$debug = $options['debug'] ?? false;
|
||||
|
||||
try {
|
||||
return KirbyTag::parse($match[0], $data, $options)->render();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// stay silent in production and ignore non-existing tags
|
||||
if ($debug !== true || Str::startsWith($e->getMessage(), 'Undefined tag type:') === true) {
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
if ($debug === true) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $match[0];
|
||||
}
|
||||
}, $text ?? '');
|
||||
}
|
||||
}
|
81
kirby/src/Text/Markdown.php
Normal file
81
kirby/src/Text/Markdown.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Text;
|
||||
|
||||
use Parsedown;
|
||||
use ParsedownExtra;
|
||||
|
||||
/**
|
||||
* The Markdown class is a wrapper around all sorts of Markdown
|
||||
* parser libraries and is meant to standardize the Markdown parser
|
||||
* API for all Kirby packages.
|
||||
*
|
||||
* It uses Parsedown and ParsedownExtra by default.
|
||||
*
|
||||
* @package Kirby Text
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class Markdown
|
||||
{
|
||||
/**
|
||||
* Array with all configured options
|
||||
* for the parser
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Returns default values for all
|
||||
* available parser options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function defaults(): array
|
||||
{
|
||||
return [
|
||||
'breaks' => true,
|
||||
'extra' => false,
|
||||
'safe' => false
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Markdown parser
|
||||
* with the given options
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = array_merge($this->defaults(), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given text and returns the HTML
|
||||
*
|
||||
* @param string|null $text
|
||||
* @param bool $inline
|
||||
* @return string
|
||||
*/
|
||||
public function parse(string $text = null, bool $inline = false): string
|
||||
{
|
||||
if ($this->options['extra'] === true) {
|
||||
$parser = new ParsedownExtra();
|
||||
} else {
|
||||
$parser = new Parsedown();
|
||||
}
|
||||
|
||||
$parser->setBreaksEnabled($this->options['breaks']);
|
||||
$parser->setSafeMode($this->options['safe']);
|
||||
|
||||
if ($inline === true) {
|
||||
return @$parser->line($text);
|
||||
} else {
|
||||
return @$parser->text($text);
|
||||
}
|
||||
}
|
||||
}
|
129
kirby/src/Text/SmartyPants.php
Normal file
129
kirby/src/Text/SmartyPants.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Text;
|
||||
|
||||
use Michelf\SmartyPantsTypographer;
|
||||
|
||||
/**
|
||||
* Wrapper for Michelf's SmartyPants
|
||||
* parser, to improve the configurability
|
||||
* of the parser with default options and
|
||||
* a simple way to set your own options.
|
||||
*
|
||||
* @package Kirby Text
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class SmartyPants
|
||||
{
|
||||
/**
|
||||
* Array with all configured options
|
||||
* for the parser
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Michelf's parser object
|
||||
*
|
||||
* @var SmartyPantsTypographer
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Returns default values for all
|
||||
* available parser options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function defaults(): array
|
||||
{
|
||||
return [
|
||||
'attr' => 1,
|
||||
'doublequote.open' => '“',
|
||||
'doublequote.close' => '”',
|
||||
'doublequote.low' => '„',
|
||||
'singlequote.open' => '‘',
|
||||
'singlequote.close' => '’',
|
||||
'backtick.doublequote.open' => '“',
|
||||
'backtick.doublequote.close' => '”',
|
||||
'backtick.singlequote.open' => '‘',
|
||||
'backtick.singlequote.close' => '’',
|
||||
'emdash' => '—',
|
||||
'endash' => '–',
|
||||
'ellipsis' => '…',
|
||||
'space' => '(?: | | |�*160;|�*[aA]0;)',
|
||||
'space.emdash' => ' ',
|
||||
'space.endash' => ' ',
|
||||
'space.colon' => ' ',
|
||||
'space.semicolon' => ' ',
|
||||
'space.marks' => ' ',
|
||||
'space.frenchquote' => ' ',
|
||||
'space.thousand' => ' ',
|
||||
'space.unit' => ' ',
|
||||
'guillemet.leftpointing' => '«',
|
||||
'guillemet.rightpointing' => '»',
|
||||
'geresh' => '׳',
|
||||
'gershayim' => '״',
|
||||
'skip' => 'pre|code|kbd|script|style|math',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SmartyPants parser
|
||||
* with the given options
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = array_merge($this->defaults(), $options);
|
||||
$this->parser = new SmartyPantsTypographer($this->options['attr']);
|
||||
|
||||
// configuration
|
||||
$this->parser->smart_doublequote_open = $this->options['doublequote.open'];
|
||||
$this->parser->smart_doublequote_close = $this->options['doublequote.close'];
|
||||
$this->parser->smart_singlequote_open = $this->options['singlequote.open'];
|
||||
$this->parser->smart_singlequote_close = $this->options['singlequote.close'];
|
||||
$this->parser->backtick_doublequote_open = $this->options['backtick.doublequote.open'];
|
||||
$this->parser->backtick_doublequote_close = $this->options['backtick.doublequote.close'];
|
||||
$this->parser->backtick_singlequote_open = $this->options['backtick.singlequote.open'];
|
||||
$this->parser->backtick_singlequote_close = $this->options['backtick.singlequote.close'];
|
||||
$this->parser->em_dash = $this->options['emdash'];
|
||||
$this->parser->en_dash = $this->options['endash'];
|
||||
$this->parser->ellipsis = $this->options['ellipsis'];
|
||||
$this->parser->tags_to_skip = $this->options['skip'];
|
||||
$this->parser->space_emdash = $this->options['space.emdash'];
|
||||
$this->parser->space_endash = $this->options['space.endash'];
|
||||
$this->parser->space_colon = $this->options['space.colon'];
|
||||
$this->parser->space_semicolon = $this->options['space.semicolon'];
|
||||
$this->parser->space_marks = $this->options['space.marks'];
|
||||
$this->parser->space_frenchquote = $this->options['space.frenchquote'];
|
||||
$this->parser->space_thousand = $this->options['space.thousand'];
|
||||
$this->parser->space_unit = $this->options['space.unit'];
|
||||
$this->parser->doublequote_low = $this->options['doublequote.low'];
|
||||
$this->parser->guillemet_leftpointing = $this->options['guillemet.leftpointing'];
|
||||
$this->parser->guillemet_rightpointing = $this->options['guillemet.rightpointing'];
|
||||
$this->parser->geresh = $this->options['geresh'];
|
||||
$this->parser->gershayim = $this->options['gershayim'];
|
||||
$this->parser->space = $this->options['space'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given text
|
||||
*
|
||||
* @param string|null $text
|
||||
* @return string
|
||||
*/
|
||||
public function parse(string $text = null): string
|
||||
{
|
||||
// prepare the text
|
||||
$text = str_replace('"', '"', $text ?? '');
|
||||
|
||||
// parse the text
|
||||
return $this->parser->transform($text);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue