Compare commits

...

17 Commits

Author SHA1 Message Date
Andy Miller
4ddc98b2b6 Merge branch 'release/1.6.24' 2020-04-27 16:08:40 -06:00
Andy Miller
d16a88e731 prepare for release 2020-04-27 16:08:28 -06:00
A----
39d0d640e6 Support for the X-Forwarded-Host (#2891) 2020-04-27 10:38:36 -06:00
Andy Miller
586105907d disable XDebug for Travis builds 2020-03-19 14:56:03 -06:00
Andy Miller
15af5bfae7 Merge branch 'release/1.6.23' 2020-03-19 14:27:39 -06:00
Andy Miller
d550a016a9 Merge tag '1.6.23' into develop
Release v1.6.23
2020-03-19 14:27:39 -06:00
Andy Miller
9c8df27bf1 Updated changelog 2020-03-19 14:27:25 -06:00
Andy Miller
1ce0176ab6 created aliases for direct \Parsedown and \ParsedownExtra references 2020-03-19 14:25:08 -06:00
Andy Miller
ccac8a93bc Merge branch 'release/1.6.23' 2020-03-19 12:46:08 -06:00
Andy Miller
bcbfa0e32a Merge tag '1.6.23' into develop
Release v1.6.23
2020-03-19 12:46:08 -06:00
Andy Miller
453cd62a51 prepare for release 2020-03-19 12:45:58 -06:00
Andy Miller
56e1cbc78e updated composer to pickup toolbox 1.4.7 2020-03-19 12:39:36 -06:00
Andy Miller
db92c7b32d Upgraded jQuery to 3.4.1 - fixes #2859 2020-03-19 11:31:10 -06:00
Andy Miller
2eae104c7a Fix for user reported CVE path-based open redirect 2020-03-18 17:32:46 -06:00
Andy Miller
6f2be2a2d2 Updated changelog 2020-03-18 11:50:23 -06:00
Andy Miller
e75960fee3 php 7.4 fixes for ParsedownExtra 2020-03-18 11:44:23 -06:00
Andy Miller
ec71ccdd0d moved parsedown 1.6 and parsedown-extra 0.7 into framework for fixes 2020-03-18 11:39:08 -06:00
14 changed files with 2187 additions and 123 deletions

View File

@@ -57,7 +57,7 @@ before_install:
fi;
fi
before_script:
- if [ $TRAVIS_PHP_VERSION != 'hhvm' ]; then phpenv config-rm xdebug.ini; fi
- phpenv config-rm xdebug.ini
script:
- if [ $TRAVIS_BRANCH == 'develop' ] || [ $TRAVIS_PULL_REQUEST != 'false' ]; then
vendor/bin/codecept run;

View File

@@ -1,3 +1,23 @@
# v1.6.24
## 04/27/2020
1. [](#improved)
* Added support for `X-Forwarded-Host` [#2891](https://github.com/getgrav/grav/pull/2891)
* Disable XDebug in Travis builds
# v1.6.23
## 03/19/2020
1. [](#new)
* Moved `Parsedown` 1.6 and `ParsedownExtra` 0.7 into `Grav\Framework\Parsedown` to allow fixes
* Added `aliases.php` with references to direct `\Parsedown` and `\ParsedownExtra` references
1. [](#improved)
* Upgraded `jQuery` to latest 3.4.1 version [#2859](https://github.com/getgrav/grav/issues/2859)
1. [](#bugfix)
* Fixed PHP 7.4 issue in ParsedownExtra [#2832](https://github.com/getgrav/grav/issues/2832)
* Fix for [user reported](https://twitter.com/OriginalSicksec) CVE path-based open redirect
* Fix for `stream_set_option` error with PHP 7.4 via Toolbox#28 [#2850](https://github.com/getgrav/grav/issues/2850)
# v1.6.22
## 03/05/2020

View File

@@ -28,8 +28,6 @@
"kodus/psr7-server": "*",
"nyholm/psr7": "^1.0",
"twig/twig": "~1.40",
"erusev/parsedown": "1.6.4",
"erusev/parsedown-extra": "~0.7",
"symfony/yaml": "~4.2.0",
"symfony/console": "~4.2.0",
"symfony/event-dispatcher": "~4.2.0",
@@ -86,7 +84,8 @@
"Grav\\": "system/src/Grav"
},
"files": [
"system/defines.php"
"system/defines.php",
"system/aliases.php"
]
},
"archive": {

168
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "943b834ae47996a8d736ba463bdf4c0a",
"content-hash": "8cf8d58f86543809e5b922b12962484d",
"packages": [
{
"name": "antoligy/dom-string-iterators",
@@ -353,95 +353,6 @@
],
"time": "2017-01-23T04:29:33+00:00"
},
{
"name": "erusev/parsedown",
"version": "1.6.4",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown.git",
"reference": "fbe3fe878f4fe69048bb8a52783a09802004f548"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/fbe3fe878f4fe69048bb8a52783a09802004f548",
"reference": "fbe3fe878f4fe69048bb8a52783a09802004f548",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"type": "library",
"autoload": {
"psr-0": {
"Parsedown": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "Parser for Markdown.",
"homepage": "http://parsedown.org",
"keywords": [
"markdown",
"parser"
],
"time": "2017-11-14T20:44:03+00:00"
},
{
"name": "erusev/parsedown-extra",
"version": "0.7.1",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown-extra.git",
"reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c",
"reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c",
"shasum": ""
},
"require": {
"erusev/parsedown": "~1.4"
},
"type": "library",
"autoload": {
"psr-0": {
"ParsedownExtra": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "An extension of Parsedown that adds support for Markdown Extra.",
"homepage": "https://github.com/erusev/parsedown-extra",
"keywords": [
"markdown",
"markdown extra",
"parsedown",
"parser"
],
"time": "2015-11-01T10:19:22+00:00"
},
{
"name": "filp/whoops",
"version": "2.7.1",
@@ -1734,16 +1645,16 @@
},
{
"name": "rockettheme/toolbox",
"version": "1.4.6",
"version": "1.4.7",
"source": {
"type": "git",
"url": "https://github.com/rockettheme/toolbox.git",
"reference": "e7010c2a956aff241dfa100ab8aef5b6cf83892a"
"reference": "6a86bc0607884d2194260b6b72d67333e0141585"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rockettheme/toolbox/zipball/e7010c2a956aff241dfa100ab8aef5b6cf83892a",
"reference": "e7010c2a956aff241dfa100ab8aef5b6cf83892a",
"url": "https://api.github.com/repos/rockettheme/toolbox/zipball/6a86bc0607884d2194260b6b72d67333e0141585",
"reference": "6a86bc0607884d2194260b6b72d67333e0141585",
"shasum": ""
},
"require": {
@@ -1780,7 +1691,7 @@
"php",
"rockettheme"
],
"time": "2019-03-20T19:59:17+00:00"
"time": "2020-03-19T18:24:40+00:00"
},
{
"name": "seld/cli-prompt",
@@ -2636,16 +2547,16 @@
"packages-dev": [
{
"name": "behat/gherkin",
"version": "v4.6.1",
"version": "v4.6.2",
"source": {
"type": "git",
"url": "https://github.com/Behat/Gherkin.git",
"reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759"
"reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Behat/Gherkin/zipball/25bdcaf37898b4a939fa3031d5d753ced97e4759",
"reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759",
"url": "https://api.github.com/repos/Behat/Gherkin/zipball/51ac4500c4dc30cbaaabcd2f25694299df666a31",
"reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31",
"shasum": ""
},
"require": {
@@ -2691,7 +2602,7 @@
"gherkin",
"parser"
],
"time": "2020-02-27T11:29:57+00:00"
"time": "2020-03-17T14:03:26+00:00"
},
{
"name": "codeception/codeception",
@@ -2900,6 +2811,12 @@
"Xdebug",
"performance"
],
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
}
],
"time": "2020-03-01T12:26:26+00:00"
},
{
@@ -3620,16 +3537,16 @@
},
{
"name": "nette/robot-loader",
"version": "v3.2.2",
"version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/nette/robot-loader.git",
"reference": "38e8a270567a4ad9fe716b40fcda5a6580afa3c0"
"reference": "726c462e73e739e965ec654a667407074cfe83c0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/robot-loader/zipball/38e8a270567a4ad9fe716b40fcda5a6580afa3c0",
"reference": "38e8a270567a4ad9fe716b40fcda5a6580afa3c0",
"url": "https://api.github.com/repos/nette/robot-loader/zipball/726c462e73e739e965ec654a667407074cfe83c0",
"reference": "726c462e73e739e965ec654a667407074cfe83c0",
"shasum": ""
},
"require": {
@@ -3679,7 +3596,7 @@
"nette",
"trait"
],
"time": "2020-02-20T22:17:50+00:00"
"time": "2020-02-28T13:10:07+00:00"
},
{
"name": "nette/schema",
@@ -4172,16 +4089,16 @@
},
{
"name": "phpspec/prophecy",
"version": "v1.10.2",
"version": "v1.10.3",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9"
"reference": "451c3cd1418cf640de218914901e51b064abb093"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
"reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
"reference": "451c3cd1418cf640de218914901e51b064abb093",
"shasum": ""
},
"require": {
@@ -4231,7 +4148,7 @@
"spy",
"stub"
],
"time": "2020-01-20T15:57:02+00:00"
"time": "2020-03-05T15:02:03+00:00"
},
{
"name": "phpstan/phpdoc-parser",
@@ -5479,6 +5396,20 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-29T10:05:28+00:00"
},
{
@@ -5528,6 +5459,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-14T07:42:58+00:00"
},
{
@@ -5685,5 +5630,6 @@
"platform-dev": [],
"platform-overrides": {
"php": "7.1.3"
}
},
"plugin-api-version": "1.1.0"
}

5
system/aliases.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
/** Moved from non-namespaced classes to Grav Framework */
class_alias(Grav\Framework\Parsedown\Parsedown::class, '\Parsedown');
class_alias(Grav\Framework\Parsedown\ParsedownExtra::class, '\ParsedownExtra');

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.6.22');
define('GRAV_VERSION', '1.6.24');
define('GRAV_TESTING', false);
define('DS', '/');

View File

@@ -11,6 +11,7 @@ namespace Grav\Common\GPM\Local;
use Grav\Common\Data\Data;
use Grav\Common\GPM\Common\Package as BasePackage;
use Grav\Framework\Parsedown\Parsedown;
class Package extends BasePackage
{
@@ -23,7 +24,7 @@ class Package extends BasePackage
$this->settings = $package->toArray();
$html_description = \Parsedown::instance()->line($this->__get('description'));
$html_description = Parsedown::instance()->line($this->__get('description'));
$this->data->set('slug', $package->__get('slug'));
$this->data->set('description_html', $html_description);
$this->data->set('description_plain', strip_tags($html_description));

View File

@@ -316,7 +316,10 @@ class Grav extends Container
/** @var Uri $uri */
$uri = $this['uri'];
//Check for code in route
// Clean route for redirect
$route = preg_replace("#^\/[\\\/]+\/#", '/', $route);
// Check for code in route
$regex = '/.*(\[(30[1-7])\])$/';
preg_match($regex, $route, $matches);
if ($matches) {

View File

@@ -11,8 +11,9 @@ namespace Grav\Common\Markdown;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Markdown\Excerpts;
use Grav\Framework\Parsedown\Parsedown as ParsedownLib;
class Parsedown extends \Parsedown
class Parsedown extends ParsedownLib
{
use ParsedownGravTrait;

View File

@@ -11,8 +11,9 @@ namespace Grav\Common\Markdown;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Markdown\Excerpts;
use Grav\Framework\Parsedown\ParsedownExtra as ParsedownExtraLib;
class ParsedownExtra extends \ParsedownExtra
class ParsedownExtra extends ParsedownExtraLib
{
use ParsedownGravTrait;

View File

@@ -1169,7 +1169,9 @@ class Uri
// Build host.
$hostname = 'localhost';
if (isset($env['HTTP_HOST'])) {
if (isset($env['HTTP_X_FORWARDED_HOST'])) {
$hostname = $env['HTTP_X_FORWARDED_HOST'];
} else if (isset($env['HTTP_HOST'])) {
$hostname = $env['HTTP_HOST'];
} elseif (isset($env['SERVER_NAME'])) {
$hostname = $env['SERVER_NAME'];

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,532 @@
<?php
/**
* @package Grav\Framework\Parsedown
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Parsedown;
/*
* Parsedown Extra
* http://parsedown.org
*
* (c) Emanuil Rusev
* http://erusev.com
*
* This file ported from officiall ParsedownExtra repo and kept for compatibility.
*/
class ParsedownExtra extends Parsedown
{
# ~
const version = '0.7.0';
# ~
function __construct()
{
if (parent::version < '1.5.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)
{
$markup = parent::text($text);
# 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 isset($Block['type']))
{
return;
}
$Element = array(
'name' => 'dl',
'handler' => 'elements',
'text' => array(),
);
$terms = explode("\n", $Block['element']['text']);
foreach ($terms as $term)
{
$Element['text'] []= array(
'name' => 'dt',
'handler' => 'line',
'text' => $term,
);
}
$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'] = 'text';
$Block['dd']['text'] .= "\n\n";
unset($Block['interrupted']);
}
$text = substr($Line['body'], min($Line['indent'], 4));
$Block['dd']['text'] .= "\n" . $text;
return $Block;
}
}
#
# Header
protected function blockHeader($Line)
{
$Block = parent::blockHeader($Line);
if ($Block !== null && preg_match('/[ #]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] = $this->parseAttributeData($attributeString);
$Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]);
}
return $Block;
}
#
# Markup
protected function blockMarkupComplete($Block)
{
if ( ! isset($Block['void']))
{
$Block['markup'] = $this->processTag($Block['markup']);
}
return $Block;
}
#
# Setext
protected function blockSetextHeader($Line, array $Block = null)
{
$Block = parent::blockSetextHeader($Line, $Block);
if ($Block !== null && preg_match('/[ ]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] = $this->parseAttributeData($attributeString);
$Block['element']['text'] = substr($Block['element']['text'], 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),
'handler' => 'element',
'text' => 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;
}
#
# ~
#
protected function unmarkedText($text)
{
$text = parent::unmarkedText($text);
if (isset($this->DefinitionData['Abbreviation']))
{
foreach ($this->DefinitionData['Abbreviation'] as $abbreviation => $meaning)
{
$pattern = '/\b'.preg_quote($abbreviation, '/').'\b/';
$text = preg_replace($pattern, '<abbr title="'.$meaning.'">'.$abbreviation.'</abbr>', $text);
}
}
return $text;
}
#
# 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' => 'line',
'text' => $text,
);
if (isset($Block['interrupted']))
{
$Block['dd']['handler'] = 'text';
unset($Block['interrupted']);
}
$Block['element']['text'] []= & $Block['dd'];
return $Block;
}
protected function buildFootnoteElement()
{
$Element = array(
'name' => 'div',
'attributes' => array('class' => 'footnotes'),
'handler' => 'elements',
'text' => array(
array(
'name' => 'hr',
),
array(
'name' => 'ol',
'handler' => 'elements',
'text' => array(),
),
),
);
uasort($this->DefinitionData['Footnote'], 'self::sortFootnotes');
foreach ($this->DefinitionData['Footnote'] as $definitionId => $DefinitionData)
{
if ( ! isset($DefinitionData['number']))
{
continue;
}
$text = $DefinitionData['text'];
$text = parent::text($text);
$numbers = range(1, $DefinitionData['count']);
$backLinksMarkup = '';
foreach ($numbers as $number)
{
$backLinksMarkup .= ' <a href="#fnref'.$number.':'.$definitionId.'" rev="footnote" class="footnote-backref">&#8617;</a>';
}
$backLinksMarkup = substr($backLinksMarkup, 1);
if (substr($text, - 4) === '</p>')
{
$backLinksMarkup = '&#160;'.$backLinksMarkup;
$text = substr_replace($text, $backLinksMarkup.'</p>', - 4);
}
else
{
$text .= "\n".'<p>'.$backLinksMarkup.'</p>';
}
$Element['text'][1]['text'] []= array(
'name' => 'li',
'attributes' => array('id' => 'fn:'.$definitionId),
'text' => "\n".$text."\n",
);
}
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]+[ ]*)';
}