mirror of
https://github.com/typecho/typecho.git
synced 2025-01-16 20:18:19 +01:00
remove commonmark
This commit is contained in:
parent
410c603cbd
commit
4fc76a4dc3
1547
admin/js/stmd.js
1547
admin/js/stmd.js
File diff suppressed because it is too large
Load Diff
@ -33,22 +33,6 @@ package:
|
||||
tar -cvvzf build.tar.gz build/
|
||||
|
||||
|
||||
commonmark-php:
|
||||
rm -rf ../var/CommonMark
|
||||
git clone https://github.com/joyqi/commonmark-php.git commonmark
|
||||
php transfer.php commonmark/src CommonMark ColinODell/CommonMark
|
||||
rm -f commonmark/src/CommonMarkConverter.php
|
||||
mv commonmark/src ../var/CommonMark
|
||||
rm -rf commonmark
|
||||
|
||||
|
||||
commonmark-js:
|
||||
rm -rf ../admin/js/stmd.js
|
||||
git clone https://github.com/jgm/stmd.git
|
||||
cp stmd/js/stmd.js ../admin/js/
|
||||
rm -rf stmd
|
||||
|
||||
|
||||
clear:
|
||||
rm -Rf build/
|
||||
|
||||
@ -63,6 +47,7 @@ upgrade:
|
||||
cp build/index.php ${DIR}
|
||||
make clear
|
||||
|
||||
|
||||
langs:
|
||||
rm -rf ../usr/langs
|
||||
mkdir ../usr/langs
|
||||
|
@ -1,599 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
|
||||
/**
|
||||
* Parses Markdown into an AST
|
||||
*/
|
||||
class CommonMark_DocParser
|
||||
{
|
||||
const CODE_INDENT = 4;
|
||||
|
||||
/**
|
||||
* @var BlockElement
|
||||
*/
|
||||
protected $tip;
|
||||
|
||||
/**
|
||||
* @var BlockElement
|
||||
*/
|
||||
protected $doc;
|
||||
|
||||
/**
|
||||
* @var InlineParser
|
||||
*/
|
||||
protected $inlineParser;
|
||||
|
||||
/**
|
||||
* @var ReferenceMap
|
||||
*/
|
||||
protected $refMap;
|
||||
|
||||
/**
|
||||
* Convert tabs to spaces on each line using a 4-space tab stop
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function detabLine($string)
|
||||
{
|
||||
if (strpos($string, "\t") === false) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
// Split into different parts
|
||||
$parts = explode("\t", $string);
|
||||
// Add each part to the resulting line
|
||||
// The first one is done here; others are prefixed
|
||||
// with the necessary spaces inside the loop below
|
||||
$line = $parts[0];
|
||||
unset($parts[0]);
|
||||
|
||||
foreach ($parts as $part) {
|
||||
// Calculate number of spaces; insert them followed by the non-tab contents
|
||||
$amount = 4 - Typecho_Common::strLen($line, 'UTF-8') % 4;
|
||||
$line .= str_repeat(' ', $amount) . $part;
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Break out of all containing lists, resetting the tip of the
|
||||
* document to the parent of the highest list, and finalizing
|
||||
* all the lists. (This is used to implement the "two blank lines
|
||||
* break of of all lists" feature.)
|
||||
*
|
||||
* @param BlockElement $block
|
||||
* @param int $lineNumber
|
||||
*/
|
||||
protected function breakOutOfLists(CommonMark_Element_BlockElement $block, $lineNumber)
|
||||
{
|
||||
$b = $block;
|
||||
$lastList = null;
|
||||
do {
|
||||
if ($b->getType() === CommonMark_Element_BlockElement::TYPE_LIST) {
|
||||
$lastList = $b;
|
||||
}
|
||||
|
||||
$b = $b->getParent();
|
||||
} while ($b);
|
||||
|
||||
if ($lastList) {
|
||||
while ($block != $lastList) {
|
||||
$this->finalize($block, $lineNumber);
|
||||
$block = $block->getParent();
|
||||
}
|
||||
$this->finalize($lastList, $lineNumber);
|
||||
$this->tip = $lastList->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ln
|
||||
* @param int $offset
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function addLine($ln, $offset)
|
||||
{
|
||||
$s = substr($ln, $offset);
|
||||
if ($s === false) {
|
||||
$s = '';
|
||||
}
|
||||
|
||||
if (!$this->tip->getIsOpen()) {
|
||||
throw new RuntimeException(sprintf('Attempted to add line (%s) to closed container.', $ln));
|
||||
}
|
||||
|
||||
$this->tip->getStrings()->add($s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
* @param int $lineNumber
|
||||
* @param int $offset
|
||||
*
|
||||
* @return BlockElement
|
||||
*/
|
||||
protected function addChild($tag, $lineNumber, $offset)
|
||||
{
|
||||
while (!$this->tip->canContain($tag)) {
|
||||
$this->finalize($this->tip, $lineNumber);
|
||||
}
|
||||
|
||||
$columnNumber = $offset + 1; // offset 0 = column 1
|
||||
$newBlock = new CommonMark_Element_BlockElement($tag, $lineNumber, $columnNumber);
|
||||
$this->tip->getChildren()->add($newBlock);
|
||||
$newBlock->setParent($this->tip);
|
||||
$this->tip = $newBlock;
|
||||
|
||||
return $newBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ln
|
||||
* @param int $offset
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function parseListMarker($ln, $offset)
|
||||
{
|
||||
$rest = substr($ln, $offset);
|
||||
$data = array();
|
||||
|
||||
if (preg_match(CommonMark_Util_RegexHelper::getInstance()->getHRuleRegex(), $rest)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($matches = CommonMark_Util_RegexHelper::matchAll('/^[*+-]( +|$)/', $rest)) {
|
||||
$spacesAfterMarker = strlen($matches[1]);
|
||||
$data['type'] = CommonMark_Element_BlockElement::LIST_TYPE_UNORDERED;
|
||||
$data['delimiter'] = null;
|
||||
$data['bullet_char'] = $matches[0][0];
|
||||
} elseif ($matches = CommonMark_Util_RegexHelper::matchAll('/^(\d+)([.)])( +|$)/', $rest)) {
|
||||
$spacesAfterMarker = strlen($matches[3]);
|
||||
$data['type'] = CommonMark_Element_BlockElement::LIST_TYPE_ORDERED;
|
||||
$data['start'] = intval($matches[1]);
|
||||
$data['delimiter'] = $matches[2];
|
||||
$data['bullet_char'] = null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$blankItem = strlen($matches[0]) === strlen($rest);
|
||||
if ($spacesAfterMarker >= 5 ||
|
||||
$spacesAfterMarker < 1 ||
|
||||
$blankItem
|
||||
) {
|
||||
$data['padding'] = strlen($matches[0]) - $spacesAfterMarker + 1;
|
||||
} else {
|
||||
$data['padding'] = strlen($matches[0]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $listData
|
||||
* @param array $itemData
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function listsMatch($listData, $itemData)
|
||||
{
|
||||
return ($listData['type'] === $itemData['type'] &&
|
||||
$listData['delimiter'] === $itemData['delimiter'] &&
|
||||
$listData['bullet_char'] === $itemData['bullet_char']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DocParser $self
|
||||
* @param mixed $oldTip
|
||||
* @param mixed $lastMatchedContainer
|
||||
* @param mixed $lineNumber
|
||||
* @param bool $closeUnmatchedBlocksAlreadyDone
|
||||
*/
|
||||
protected function closeUnmatchedBlocks(CommonMark_DocParser $self, $oldTip, $lastMatchedContainer, $lineNumber, &$closeUnmatchedBlocksAlreadyDone)
|
||||
{
|
||||
// finalize any blocks not matched
|
||||
while (!$closeUnmatchedBlocksAlreadyDone && $oldTip != $lastMatchedContainer && $oldTip !== null) {
|
||||
$self->finalize($oldTip, $lineNumber);
|
||||
$oldTip = $oldTip->getParent();
|
||||
}
|
||||
$closeUnmatchedBlocksAlreadyDone = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ln
|
||||
* @param int $lineNumber
|
||||
*/
|
||||
protected function incorporateLine($ln, $lineNumber)
|
||||
{
|
||||
$allMatched = true;
|
||||
$offset = 0;
|
||||
$blank = false;
|
||||
$container = $this->doc;
|
||||
$oldTip = $this->tip;
|
||||
|
||||
// Convert tabs to spaces:
|
||||
$ln = self::detabLine($ln);
|
||||
|
||||
// For each containing block, try to parse the associated line start.
|
||||
// Bail out on failure: container will point to the last matching block.
|
||||
// Set all_matched to false if not all containers match.
|
||||
while ($container->hasChildren()) {
|
||||
/** @var BlockElement $lastChild */
|
||||
$lastChild = $container->getChildren()->last();
|
||||
if (!$lastChild->getIsOpen()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$container = $lastChild;
|
||||
|
||||
$match = CommonMark_Util_RegexHelper::matchAt('/[^ ]/', $ln, $offset);
|
||||
if ($match === null) {
|
||||
$firstNonSpace = strlen($ln);
|
||||
$blank = true;
|
||||
} else {
|
||||
$firstNonSpace = $match;
|
||||
$blank = false;
|
||||
}
|
||||
|
||||
$indent = $firstNonSpace - $offset;
|
||||
|
||||
switch ($container->getType()) {
|
||||
case CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE:
|
||||
$matched = $indent <= 3 && isset($ln[$firstNonSpace]) && $ln[$firstNonSpace] === '>';
|
||||
if ($matched) {
|
||||
$offset = $firstNonSpace + 1;
|
||||
if (isset($ln[$offset]) && $ln[$offset] === ' ') {
|
||||
$offset++;
|
||||
}
|
||||
} else {
|
||||
$allMatched = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_LIST_ITEM:
|
||||
$listData = $container->getExtra('list_data');
|
||||
$increment = $listData['marker_offset'] + $listData['padding'];
|
||||
if ($indent >= $increment) {
|
||||
$offset += $increment;
|
||||
} elseif ($blank) {
|
||||
$offset = $firstNonSpace;
|
||||
} else {
|
||||
$allMatched = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_INDENTED_CODE:
|
||||
if ($indent >= self::CODE_INDENT) {
|
||||
$offset += self::CODE_INDENT;
|
||||
} elseif ($blank) {
|
||||
$offset = $firstNonSpace;
|
||||
} else {
|
||||
$allMatched = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_ATX_HEADER:
|
||||
case CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER:
|
||||
case CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE:
|
||||
// a header can never contain > 1 line, so fail to match:
|
||||
$allMatched = false;
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_FENCED_CODE:
|
||||
// skip optional spaces of fence offset
|
||||
$i = $container->getExtra('fence_offset');
|
||||
while ($i > 0 && $ln[$offset] === ' ') {
|
||||
$offset++;
|
||||
$i--;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_HTML_BLOCK:
|
||||
if ($blank) {
|
||||
$allMatched = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_PARAGRAPH:
|
||||
if ($blank) {
|
||||
$container->setIsLastLineBlank(true);
|
||||
$allMatched = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Nothing
|
||||
}
|
||||
|
||||
if (!$allMatched) {
|
||||
$container = $container->getParent(); // back up to the last matching block
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$lastMatchedContainer = $container;
|
||||
|
||||
// This function is used to finalize and close any unmatched
|
||||
// blocks. We aren't ready to do this now, because we might
|
||||
// have a lazy paragraph continuation, in which case we don't
|
||||
// want to close unmatched blocks. So we store this closure for
|
||||
// use later, when we have more information.
|
||||
$closeUnmatchedBlocksAlreadyDone = false;
|
||||
|
||||
// Check to see if we've hit 2nd blank line; if so break out of list:
|
||||
if ($blank && $container->getIsLastLineBlank()) {
|
||||
$this->breakOutOfLists($container, $lineNumber);
|
||||
}
|
||||
|
||||
// Unless last matched container is a code block, try new container starts,
|
||||
// adding children to the last matched container:
|
||||
while ($container->getType() != CommonMark_Element_BlockElement::TYPE_FENCED_CODE &&
|
||||
$container->getType() != CommonMark_Element_BlockElement::TYPE_INDENTED_CODE &&
|
||||
$container->getType() != CommonMark_Element_BlockElement::TYPE_HTML_BLOCK &&
|
||||
// this is a little performance optimization
|
||||
CommonMark_Util_RegexHelper::matchAt('/^[ #`~*+_=<>0-9-]/', $ln, $offset) !== null
|
||||
) {
|
||||
$match = CommonMark_Util_RegexHelper::matchAt('/[^ ]/', $ln, $offset);
|
||||
if ($match === null) {
|
||||
$firstNonSpace = strlen($ln);
|
||||
$blank = true;
|
||||
} else {
|
||||
$firstNonSpace = $match;
|
||||
$blank = false;
|
||||
}
|
||||
|
||||
$indent = $firstNonSpace - $offset;
|
||||
|
||||
if ($indent >= self::CODE_INDENT) {
|
||||
// indented code
|
||||
if ($this->tip->getType() != CommonMark_Element_BlockElement::TYPE_PARAGRAPH && !$blank) {
|
||||
$offset += self::CODE_INDENT;
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_INDENTED_CODE, $lineNumber, $offset);
|
||||
} else { // ident > 4 in a lazy paragraph continuation
|
||||
break;
|
||||
}
|
||||
} elseif (!$blank && $ln[$firstNonSpace] === '>') {
|
||||
// blockquote
|
||||
$offset = $firstNonSpace + 1;
|
||||
// optional following space
|
||||
if (isset($ln[$offset]) && $ln[$offset] === ' ') {
|
||||
$offset++;
|
||||
}
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE, $lineNumber, $offset);
|
||||
} elseif ($match = CommonMark_Util_RegexHelper::matchAll('/^#{1,6}(?: +|$)/', $ln, $firstNonSpace)) {
|
||||
// ATX header
|
||||
$offset = $firstNonSpace + strlen($match[0]);
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_ATX_HEADER, $lineNumber, $firstNonSpace);
|
||||
$container->setExtra('level', strlen(trim($match[0]))); // number of #s
|
||||
// remove trailing ###s
|
||||
$container->getStrings()->add(
|
||||
preg_replace(
|
||||
'/(?:(\\\\#) *#*| *#+) *$/',
|
||||
'$1',
|
||||
substr($ln, $offset)
|
||||
)
|
||||
);
|
||||
break;
|
||||
} elseif ($match = CommonMark_Util_RegexHelper::matchAll('/^`{3,}(?!.*`)|^~{3,}(?!.*~)/', $ln, $firstNonSpace)) {
|
||||
// fenced code block
|
||||
$fenceLength = strlen($match[0]);
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_FENCED_CODE, $lineNumber, $firstNonSpace);
|
||||
$container->setExtra('fence_length', $fenceLength);
|
||||
$container->setExtra('fence_char', $match[0][0]);
|
||||
$container->setExtra('fence_offset', $firstNonSpace - $offset);
|
||||
$offset = $firstNonSpace + $fenceLength;
|
||||
break;
|
||||
} elseif (CommonMark_Util_RegexHelper::matchAt(
|
||||
CommonMark_Util_RegexHelper::getInstance()->getHtmlBlockOpenRegex(),
|
||||
$ln,
|
||||
$firstNonSpace
|
||||
) !== null
|
||||
) {
|
||||
// html block
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_HTML_BLOCK, $lineNumber, $firstNonSpace);
|
||||
// note, we don't adjust offset because the tag is part of the text
|
||||
break;
|
||||
} elseif ($container->getType() === CommonMark_Element_BlockElement::TYPE_PARAGRAPH &&
|
||||
$container->getStrings()->count() === 1 &&
|
||||
($match = CommonMark_Util_RegexHelper::matchAll('/^(?:=+|-+) *$/', $ln, $firstNonSpace))
|
||||
) {
|
||||
// setext header line
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$container->setType(CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER);
|
||||
$container->setExtra('level', $match[0][0] === '=' ? 1 : 2);
|
||||
$offset = strlen($ln);
|
||||
} elseif (CommonMark_Util_RegexHelper::matchAt(CommonMark_Util_RegexHelper::getInstance()->getHRuleRegex(), $ln, $firstNonSpace) !== null) {
|
||||
// hrule
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE, $lineNumber, $firstNonSpace);
|
||||
$offset = strlen($ln) - 1;
|
||||
break;
|
||||
} elseif (($data = $this->parseListMarker($ln, $firstNonSpace))) {
|
||||
// list item
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
$data['marker_offset'] = $indent;
|
||||
$offset = $firstNonSpace + $data['padding'];
|
||||
|
||||
// add the list if needed
|
||||
if ($container->getType() !== CommonMark_Element_BlockElement::TYPE_LIST ||
|
||||
!($this->listsMatch($container->getExtra('list_data'), $data))
|
||||
) {
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_LIST, $lineNumber, $firstNonSpace);
|
||||
$container->setExtra('list_data', $data);
|
||||
}
|
||||
|
||||
// add the list item
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_LIST_ITEM, $lineNumber, $firstNonSpace);
|
||||
$container->setExtra('list_data', ($data));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($container->acceptsLines()) {
|
||||
// if it's a line container, it can't contain other containers
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// What remains at the offset is a text line. Add the text to the appropriate container.
|
||||
|
||||
$match = CommonMark_Util_RegexHelper::matchAt('/[^ ]/', $ln, $offset);
|
||||
if ($match === null) {
|
||||
$firstNonSpace = strlen($ln);
|
||||
$blank = true;
|
||||
} else {
|
||||
$firstNonSpace = $match;
|
||||
$blank = false;
|
||||
}
|
||||
|
||||
$indent = $firstNonSpace - $offset;
|
||||
|
||||
// First check for a lazy paragraph continuation:
|
||||
if ($this->tip !== $lastMatchedContainer &&
|
||||
!$blank &&
|
||||
$this->tip->getType() == CommonMark_Element_BlockElement::TYPE_PARAGRAPH &&
|
||||
$this->tip->getStrings()->count() > 0
|
||||
) {
|
||||
// lazy paragraph continuation
|
||||
$this->lastLineBlank = false; // TODO: really? (see line 1152)
|
||||
$this->addLine($ln, $offset);
|
||||
} else { // not a lazy continuation
|
||||
//finalize any blocks not matched
|
||||
$this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone);
|
||||
|
||||
// Block quote lines are never blank as they start with >
|
||||
// and we don't count blanks in fenced code for purposes of tight/loose
|
||||
// lists or breaking out of lists. We also don't set last_line_blank
|
||||
// on an empty list item.
|
||||
$container->setIsLastLineBlank(
|
||||
$blank &&
|
||||
!(
|
||||
$container->getType() == CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE ||
|
||||
$container->getType() == CommonMark_Element_BlockElement::TYPE_FENCED_CODE ||
|
||||
($container->getType() == CommonMark_Element_BlockElement::TYPE_LIST_ITEM &&
|
||||
$container->getChildren()->count() === 0 &&
|
||||
$container->getStartLine() == $lineNumber
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$cont = $container;
|
||||
while ($cont->getParent()) {
|
||||
$cont->getParent()->setIsLastLineBlank(false);
|
||||
$cont = $cont->getParent();
|
||||
}
|
||||
|
||||
switch ($container->getType()) {
|
||||
case CommonMark_Element_BlockElement::TYPE_INDENTED_CODE:
|
||||
case CommonMark_Element_BlockElement::TYPE_HTML_BLOCK:
|
||||
$this->addLine($ln, $offset);
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_FENCED_CODE:
|
||||
// check for closing code fence
|
||||
$test = ($indent <= 3 &&
|
||||
isset($ln[$firstNonSpace]) &&
|
||||
$ln[$firstNonSpace] == $container->getExtra('fence_char') &&
|
||||
$match = CommonMark_Util_RegexHelper::matchAll('/^(?:`{3,}|~{3,})(?= *$)/', $ln, $firstNonSpace)
|
||||
);
|
||||
if ($test && strlen($match[0]) >= $container->getExtra('fence_length')) {
|
||||
// don't add closing fence to container; instead, close it:
|
||||
$this->finalize($container, $lineNumber);
|
||||
} else {
|
||||
$this->addLine($ln, $offset);
|
||||
}
|
||||
break;
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_ATX_HEADER:
|
||||
case CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER:
|
||||
case CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE:
|
||||
// nothing to do; we already added the contents.
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($container->acceptsLines()) {
|
||||
$this->addLine($ln, $firstNonSpace);
|
||||
} elseif ($blank) {
|
||||
// do nothing
|
||||
} elseif ($container->getType() != CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE && $container->getType(
|
||||
) != CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER
|
||||
) {
|
||||
// create paragraph container for line
|
||||
$container = $this->addChild(CommonMark_Element_BlockElement::TYPE_PARAGRAPH, $lineNumber, $firstNonSpace);
|
||||
$this->addLine($ln, $firstNonSpace);
|
||||
} else {
|
||||
// TODO: throw exception?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockElement $block
|
||||
* @param int $lineNumber
|
||||
*/
|
||||
public function finalize(CommonMark_Element_BlockElement $block, $lineNumber)
|
||||
{
|
||||
$block->finalize($lineNumber, $this->inlineParser, $this->refMap);
|
||||
|
||||
$this->tip = $block->getParent(); // typo on 1310?
|
||||
}
|
||||
|
||||
/**
|
||||
* The main parsing function. Returns a parsed document AST.
|
||||
* @param string $input
|
||||
*
|
||||
* @return BlockElement
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function parse($input)
|
||||
{
|
||||
$this->doc = new CommonMark_Element_BlockElement(CommonMark_Element_BlockElement::TYPE_DOCUMENT, 1, 1);
|
||||
$this->tip = $this->doc;
|
||||
|
||||
$this->inlineParser = new CommonMark_InlineParser();
|
||||
$this->refMap = new CommonMark_Reference_ReferenceMap();
|
||||
|
||||
// Remove any /n which appears at the very end of the string
|
||||
if (substr($input, -1) == "\n") {
|
||||
$input = substr($input, 0, -1);
|
||||
}
|
||||
|
||||
$lines = preg_split('/\r\n|\n|\r/', $input);
|
||||
$len = count($lines);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$this->incorporateLine($lines[$i], $i + 1);
|
||||
}
|
||||
|
||||
while ($this->tip) {
|
||||
$this->finalize($this->tip, $len - 1);
|
||||
}
|
||||
|
||||
$this->doc->processInlines($this->inlineParser, $this->refMap);
|
||||
|
||||
return $this->doc;
|
||||
}
|
||||
}
|
@ -1,478 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
|
||||
/**
|
||||
* Block-level element
|
||||
*/
|
||||
class CommonMark_Element_BlockElement
|
||||
{
|
||||
const TYPE_ATX_HEADER = 'ATXHeader';
|
||||
const TYPE_BLOCK_QUOTE = 'BlockQuote';
|
||||
const TYPE_DOCUMENT = 'Document';
|
||||
const TYPE_FENCED_CODE = 'FencedCode';
|
||||
const TYPE_HORIZONTAL_RULE = 'HorizontalRule';
|
||||
const TYPE_HTML_BLOCK = 'HtmlBlock';
|
||||
const TYPE_INDENTED_CODE = 'IndentedCode';
|
||||
const TYPE_LIST = 'List';
|
||||
const TYPE_LIST_ITEM = 'ListItem';
|
||||
const TYPE_PARAGRAPH = 'Paragraph';
|
||||
const TYPE_REFERENCE_DEF = 'ReferenceDef';
|
||||
const TYPE_SETEXT_HEADER = 'SetextHeader';
|
||||
|
||||
const LIST_TYPE_ORDERED = 'Outline';
|
||||
const LIST_TYPE_UNORDERED = 'Bullet';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $open = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $lastLineBlank = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $startLine;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $startColumn;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $endLine;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection|BlockELement[]
|
||||
*/
|
||||
protected $children;
|
||||
|
||||
/**
|
||||
* @var BlockElement|null
|
||||
*/
|
||||
protected $parent = null;
|
||||
|
||||
/**
|
||||
* This is formed by concatenating strings, in finalize:
|
||||
* @var string
|
||||
*/
|
||||
protected $stringContent = '';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $strings;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection|InlineElementInterface[]
|
||||
*/
|
||||
protected $inlineContent;
|
||||
|
||||
/**
|
||||
* Extra data storage
|
||||
* @var array
|
||||
*/
|
||||
protected $extras = array();
|
||||
|
||||
/**
|
||||
* Constrcutor
|
||||
*
|
||||
* @param string $type Block type (see TYPE_ constants)
|
||||
* @param int $startLine Line where the block element starts
|
||||
* @param int $startColumn Column where the block element starts
|
||||
*/
|
||||
public function __construct($type, $startLine, $startColumn)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->startLine = $startLine;
|
||||
$this->startColumn = $startColumn;
|
||||
$this->endLine = $startLine;
|
||||
|
||||
$this->children = new CommonMark_Util_ArrayCollection();
|
||||
$this->strings = new CommonMark_Util_ArrayCollection();
|
||||
$this->inlineContent = new CommonMark_Util_ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if parent block can contain child block
|
||||
*
|
||||
* @param mixed $childType The type of child block to add (see TYPE_ constants)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canContain($childType)
|
||||
{
|
||||
$parentType = $this->type;
|
||||
|
||||
return ($parentType == self::TYPE_DOCUMENT ||
|
||||
$parentType == self::TYPE_BLOCK_QUOTE ||
|
||||
$parentType == self::TYPE_LIST_ITEM ||
|
||||
($parentType == self::TYPE_LIST && $childType == self::TYPE_LIST_ITEM));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if block type can accept lines of text
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function acceptsLines()
|
||||
{
|
||||
return ($this->type == self::TYPE_PARAGRAPH ||
|
||||
$this->type == self::TYPE_INDENTED_CODE ||
|
||||
$this->type == self::TYPE_FENCED_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the block ends with a blank line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function endsWithBlankLine()
|
||||
{
|
||||
if ($this->lastLineBlank) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (($this->type == self::TYPE_LIST || $this->type == self::TYPE_LIST_ITEM) && $this->hasChildren()) {
|
||||
return $this->getChildren()->last()->endsWithBlankLine();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection|BlockElement[]
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
return !$this->children->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BlockElement|null
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent ? $this->parent : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the block is open for modifications
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsOpen()
|
||||
{
|
||||
return $this->open;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection|string[]
|
||||
*/
|
||||
public function getStrings()
|
||||
{
|
||||
return $this->strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockElement $parent
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(CommonMark_Element_BlockElement $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsLastLineBlank()
|
||||
{
|
||||
return $this->lastLineBlank;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIsLastLineBlank($value)
|
||||
{
|
||||
$this->lastLineBlank = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIsOpen($value)
|
||||
{
|
||||
$this->open = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getStartLine()
|
||||
{
|
||||
return $this->startLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lineNumber
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEndLine($lineNumber)
|
||||
{
|
||||
$this->endLine = $lineNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection|InlineElementInterface[]
|
||||
*/
|
||||
public function getInlineContent()
|
||||
{
|
||||
return $this->inlineContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getExtra($key)
|
||||
{
|
||||
return isset($this->extras[$key]) ? $this->extras[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setExtra($key, $value)
|
||||
{
|
||||
$this->extras[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getStringContent()
|
||||
{
|
||||
return $this->stringContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if string contains only space characters
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isStringContentBlank()
|
||||
{
|
||||
return preg_match('/^\s*$/', $this->stringContent) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize the block; mark it closed for modification
|
||||
*
|
||||
* @param int $lineNumber
|
||||
* @param InlineParser $inlineParser
|
||||
* @param ReferenceMap $refMap
|
||||
*/
|
||||
public function finalize($lineNumber, CommonMark_InlineParser $inlineParser, CommonMark_Reference_ReferenceMap $refMap)
|
||||
{
|
||||
if (!$this->open) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->open = false;
|
||||
if ($lineNumber > $this->startLine) {
|
||||
$this->endLine = $lineNumber - 1;
|
||||
} else {
|
||||
$this->endLine = $lineNumber;
|
||||
}
|
||||
|
||||
switch ($this->getType()) {
|
||||
case self::TYPE_PARAGRAPH:
|
||||
$this->stringContent = preg_replace(
|
||||
'/^ */m',
|
||||
'',
|
||||
implode("\n", $this->strings->toArray())
|
||||
);
|
||||
|
||||
// Try parsing the beginning as link reference definitions:
|
||||
while ($this->stringContent[0] === '[' &&
|
||||
($pos = $inlineParser->parseReference($this->stringContent, $refMap))
|
||||
) {
|
||||
$this->stringContent = substr($this->stringContent, $pos);
|
||||
if ($this->isStringContentBlank()) { //RegexHelper::getInstance()->isBlank($this->stringContent)) {
|
||||
$this->type = self::TYPE_REFERENCE_DEF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case self::TYPE_ATX_HEADER:
|
||||
case self::TYPE_SETEXT_HEADER:
|
||||
case self::TYPE_HTML_BLOCK:
|
||||
$this->stringContent = implode("\n", $this->strings->toArray());
|
||||
break;
|
||||
|
||||
case self::TYPE_INDENTED_CODE:
|
||||
$reversed = array_reverse($this->strings->toArray(), true);
|
||||
foreach ($reversed as $index => $line) {
|
||||
if ($line == '' || $line === "\n" || preg_match('/^(\n *)$/', $line)) {
|
||||
unset($reversed[$index]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$fixed = array_reverse($reversed);
|
||||
$tmp = implode("\n", $fixed);
|
||||
if (substr($tmp, -1) !== "\n") {
|
||||
$tmp .= "\n";
|
||||
}
|
||||
|
||||
$this->stringContent = $tmp;
|
||||
break;
|
||||
|
||||
case self::TYPE_FENCED_CODE:
|
||||
// first line becomes info string
|
||||
$this->setExtra('info', CommonMark_Util_RegexHelper::unescape(trim($this->strings->first())));
|
||||
if ($this->strings->count() == 1) {
|
||||
$this->stringContent = '';
|
||||
} else {
|
||||
$this->stringContent = implode("\n", $this->strings->slice(1)) . "\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case self::TYPE_LIST:
|
||||
$this->setExtra('tight', true); // tight by default
|
||||
|
||||
$numItems = $this->children->count();
|
||||
$i = 0;
|
||||
while ($i < $numItems) {
|
||||
/** @var BlockElement $item */
|
||||
$item = $this->children->get($i);
|
||||
// check for non-final list item ending with blank line:
|
||||
$lastItem = $i == $numItems - 1;
|
||||
if ($item->endsWithBlankLine() && !$lastItem) {
|
||||
$this->setExtra('tight', false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Recurse into children of list item, to see if there are
|
||||
// spaces between any of them:
|
||||
$numSubItems = $item->getChildren()->count();
|
||||
$j = 0;
|
||||
while ($j < $numSubItems) {
|
||||
$subItem = $item->getChildren()->get($j);
|
||||
$lastSubItem = $j == $numSubItems - 1;
|
||||
if ($subItem->endsWithBlankLine() && !($lastItem && $lastSubItem)) {
|
||||
$this->setExtra('tight', false);
|
||||
break;
|
||||
}
|
||||
|
||||
$j++;
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InlineParser $inlineParser
|
||||
* @param ReferenceMap $refMap
|
||||
*/
|
||||
public function processInlines(CommonMark_InlineParser $inlineParser, CommonMark_Reference_ReferenceMap $refMap)
|
||||
{
|
||||
switch ($this->getType()) {
|
||||
case self::TYPE_PARAGRAPH:
|
||||
case self::TYPE_SETEXT_HEADER:
|
||||
case self::TYPE_ATX_HEADER:
|
||||
$this->inlineContent = $inlineParser->parse(trim($this->stringContent), $refMap);
|
||||
$this->stringContent = '';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->hasChildren()) {
|
||||
foreach ($this->getChildren() as $child) {
|
||||
$child->processInlines($inlineParser, $refMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
|
||||
/**
|
||||
* Provides static methods to simplify and standardize the creation of inline elements
|
||||
*/
|
||||
class CommonMark_Element_InlineCreator
|
||||
{
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createCode($code)
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_CODE, array('c' => $code));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $contents
|
||||
*
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createEmph($contents)
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_EMPH, array('c' => $contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $contents
|
||||
*
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createEntity($contents)
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_ENTITY, array('c' => $contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createHardbreak()
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_HARDBREAK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $html
|
||||
*
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createHtml($html)
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_HTML, array('c' => $html));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $destination
|
||||
* @param string|ArrayCollection|null $label
|
||||
* @param string|null $title
|
||||
*
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createLink($destination, $label = null, $title = null)
|
||||
{
|
||||
$attr = array('destination' => $destination);
|
||||
|
||||
if (is_string($label)) {
|
||||
$attr['label'] = array(self::createString($label));
|
||||
} elseif (is_object($label) && $label instanceof CommonMark_Util_ArrayCollection) {
|
||||
$attr['label'] = $label->toArray();
|
||||
} elseif (empty($label)) {
|
||||
$attr['label'] = array(self::createString($destination));
|
||||
} else {
|
||||
$attr['label'] = $label;
|
||||
}
|
||||
|
||||
if ($title) {
|
||||
$attr['title'] = $title;
|
||||
}
|
||||
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_LINK, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createSoftbreak()
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_SOFTBREAK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $contents
|
||||
*
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createString($contents)
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_STRING, array('c' => $contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $contents
|
||||
*
|
||||
* @return InlineElement
|
||||
*/
|
||||
public static function createStrong($contents)
|
||||
{
|
||||
return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_STRONG, array('c' => $contents));
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
/**
|
||||
* Inline element
|
||||
*/
|
||||
class CommonMark_Element_InlineElement implements CommonMark_Element_InlineElementInterface
|
||||
{
|
||||
const TYPE_CODE = 'Code';
|
||||
const TYPE_EMPH = 'Emph';
|
||||
const TYPE_ENTITY = 'Entity';
|
||||
const TYPE_HARDBREAK = 'Hardbreak';
|
||||
const TYPE_HTML = 'Html';
|
||||
const TYPE_IMAGE = 'Image';
|
||||
const TYPE_LINK = 'Link';
|
||||
const TYPE_SOFTBREAK = 'Softbreak';
|
||||
const TYPE_STRING = 'Str';
|
||||
const TYPE_STRONG = 'Strong';
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes;
|
||||
|
||||
/**
|
||||
* @param mixed $type
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($type, array $attributes = array())
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
return $this->getAttribute('c');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $contents
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContents($contents)
|
||||
{
|
||||
$this->setAttribute('c', $contents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attrName
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getAttribute($attrName)
|
||||
{
|
||||
return isset($this->attributes[$attrName]) ? $this->attributes[$attrName] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attrName
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($attrName, $value)
|
||||
{
|
||||
$this->attributes[$attrName] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
interface CommonMark_Element_InlineElementInterface
|
||||
{
|
||||
/**
|
||||
* @return $string
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContents();
|
||||
|
||||
/**
|
||||
* @param mixed $contents
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContents($contents);
|
||||
|
||||
/**
|
||||
* @param string $attrName
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute($attrName);
|
||||
|
||||
/**
|
||||
* @param string $attrName
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($attrName, $value);
|
||||
}
|
@ -1,261 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
|
||||
/**
|
||||
* Renders a parsed AST to HTML
|
||||
*/
|
||||
class CommonMark_HtmlRenderer
|
||||
{
|
||||
protected $blockSeparator = "\n";
|
||||
protected $innerSeparator = "\n";
|
||||
protected $softBreak = "\n";
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @param bool $preserveEntities
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @todo: Can we use simple find/replace instead?
|
||||
*/
|
||||
protected function escape($string, $preserveEntities = false)
|
||||
{
|
||||
if ($preserveEntities) {
|
||||
$string = preg_replace('/[&](?![#](x[a-f0-9]{1,8}|[0-9]{1,8});|[a-z][a-z0-9]{1,31};)/i', '&', $string);
|
||||
} else {
|
||||
$string = preg_replace('/[&]/', '&', $string);
|
||||
}
|
||||
|
||||
$string = preg_replace('/[<]/', '<', $string);
|
||||
$string = preg_replace('/[>]/', '>', $string);
|
||||
$string = preg_replace('/["]/', '"', $string);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to produce content in a pair of HTML tags.
|
||||
*
|
||||
* @param string $tag
|
||||
* @param array $attribs
|
||||
* @param string|null $contents
|
||||
* @param bool $selfClosing
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function inTags($tag, $attribs = array(), $contents = null, $selfClosing = false)
|
||||
{
|
||||
$result = '<' . $tag;
|
||||
|
||||
foreach ($attribs as $key => $value) {
|
||||
$result .= ' ' . $key . '="' . $value . '"';
|
||||
|
||||
}
|
||||
|
||||
if ($contents) {
|
||||
$result .= '>' . $contents . '</' . $tag . '>';
|
||||
} elseif ($selfClosing) {
|
||||
$result .= ' />';
|
||||
} else {
|
||||
$result .= '></' . $tag . '>';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InlineElementInterface $inline
|
||||
*
|
||||
* @return mixed|string
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function renderInline(CommonMark_Element_InlineElementInterface $inline)
|
||||
{
|
||||
$attrs = array();
|
||||
switch ($inline->getType()) {
|
||||
case CommonMark_Element_InlineElement::TYPE_STRING:
|
||||
return $this->escape($inline->getContents());
|
||||
case CommonMark_Element_InlineElement::TYPE_SOFTBREAK:
|
||||
return $this->softBreak;
|
||||
case CommonMark_Element_InlineElement::TYPE_HARDBREAK:
|
||||
return $this->inTags('br', array(), '', true) . "\n";
|
||||
case CommonMark_Element_InlineElement::TYPE_EMPH:
|
||||
return $this->inTags('em', array(), $this->renderInlines($inline->getContents()));
|
||||
case CommonMark_Element_InlineElement::TYPE_STRONG:
|
||||
return $this->inTags('strong', array(), $this->renderInlines($inline->getContents()));
|
||||
case CommonMark_Element_InlineElement::TYPE_HTML:
|
||||
return $inline->getContents();
|
||||
case CommonMark_Element_InlineElement::TYPE_ENTITY:
|
||||
return $inline->getContents();
|
||||
case CommonMark_Element_InlineElement::TYPE_LINK:
|
||||
$attrs['href'] = $this->escape($inline->getAttribute('destination'), true);
|
||||
if ($title = $inline->getAttribute('title')) {
|
||||
$attrs['title'] = $this->escape($title, true);
|
||||
}
|
||||
|
||||
return $this->inTags('a', $attrs, $this->renderInlines($inline->getAttribute('label')));
|
||||
case CommonMark_Element_InlineElement::TYPE_IMAGE:
|
||||
$attrs['src'] = $this->escape($inline->getAttribute('destination'), true);
|
||||
$attrs['alt'] = $this->escape($this->renderInlines($inline->getAttribute('label')));
|
||||
if ($title = $inline->getAttribute('title')) {
|
||||
$attrs['title'] = $this->escape($title, true);
|
||||
}
|
||||
|
||||
return $this->inTags('img', $attrs, '', true);
|
||||
case CommonMark_Element_InlineElement::TYPE_CODE:
|
||||
return $this->inTags('code', array(), $this->escape($inline->getContents()));
|
||||
default:
|
||||
throw new InvalidArgumentException('Unknown inline type: ' . $inline->getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InlineElement[] $inlines
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function renderInlines($inlines)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($inlines as $inline) {
|
||||
$result[] = $this->renderInline($inline);
|
||||
}
|
||||
|
||||
return implode('', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockElement $block
|
||||
* @param bool $inTightList
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function renderBlock(CommonMark_Element_BlockElement $block, $inTightList = false)
|
||||
{
|
||||
switch ($block->getType()) {
|
||||
case CommonMark_Element_BlockElement::TYPE_DOCUMENT:
|
||||
$wholeDoc = $this->renderBlocks($block->getChildren());
|
||||
|
||||
return $wholeDoc === '' ? '' : $wholeDoc . "\n";
|
||||
case CommonMark_Element_BlockElement::TYPE_PARAGRAPH:
|
||||
if ($inTightList) {
|
||||
return $this->renderInlines($block->getInlineContent());
|
||||
} else {
|
||||
return $this->inTags('p', array(), $this->renderInlines($block->getInlineContent()));
|
||||
}
|
||||
break;
|
||||
case CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE:
|
||||
$filling = $this->renderBlocks($block->getChildren());
|
||||
if ($filling === '') {
|
||||
return $this->inTags('blockquote', array(), $this->innerSeparator);
|
||||
} else {
|
||||
return $this->inTags(
|
||||
'blockquote',
|
||||
array(),
|
||||
$this->innerSeparator . $filling . $this->innerSeparator
|
||||
);
|
||||
}
|
||||
case CommonMark_Element_BlockElement::TYPE_LIST_ITEM:
|
||||
return trim($this->inTags('li', array(), $this->renderBlocks($block->getChildren(), $inTightList)));
|
||||
case CommonMark_Element_BlockElement::TYPE_LIST:
|
||||
$listData = $block->getExtra('list_data');
|
||||
$start = isset($listData['start']) ? $listData['start'] : null;
|
||||
|
||||
$tag = $listData['type'] == CommonMark_Element_BlockElement::LIST_TYPE_UNORDERED ? 'ul' : 'ol';
|
||||
$attr = (!$start || $start == 1) ?
|
||||
array() : array('start' => (string)$start);
|
||||
|
||||
return $this->inTags(
|
||||
$tag,
|
||||
$attr,
|
||||
$this->innerSeparator . $this->renderBlocks(
|
||||
$block->getChildren(),
|
||||
$block->getExtra('tight')
|
||||
) . $this->innerSeparator
|
||||
);
|
||||
case CommonMark_Element_BlockElement::TYPE_ATX_HEADER:
|
||||
case CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER:
|
||||
$tag = 'h' . $block->getExtra('level');
|
||||
|
||||
return $this->inTags($tag, array(), $this->renderInlines($block->getInlineContent()));
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_INDENTED_CODE:
|
||||
return $this->inTags(
|
||||
'pre',
|
||||
array(),
|
||||
$this->inTags('code', array(), $this->escape($block->getStringContent()))
|
||||
);
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_FENCED_CODE:
|
||||
$infoWords = preg_split('/ +/', $block->getExtra('info'));
|
||||
$attr = count($infoWords) === 0 || strlen(
|
||||
$infoWords[0]
|
||||
) === 0 ? array() : array('class' => 'language-' . $this->escape($infoWords[0], true));
|
||||
return $this->inTags(
|
||||
'pre',
|
||||
array(),
|
||||
$this->inTags('code', $attr, $this->escape($block->getStringContent()))
|
||||
);
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_HTML_BLOCK:
|
||||
return $block->getStringContent();
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_REFERENCE_DEF:
|
||||
return '';
|
||||
|
||||
case CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE:
|
||||
return $this->inTags('hr', array(), '', true);
|
||||
|
||||
default:
|
||||
throw new RuntimeException('Unknown block type: ' . $block->getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockElement[] $blocks
|
||||
* @param bool $inTightList
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function renderBlocks($blocks, $inTightList = false)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($blocks as $block) {
|
||||
if ($block->getType() !== 'ReferenceDef') {
|
||||
$result[] = $this->renderBlock($block, $inTightList);
|
||||
}
|
||||
}
|
||||
|
||||
return implode($this->blockSeparator, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockElement $block
|
||||
* @param bool $inTightList
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function render(CommonMark_Element_BlockElement $block, $inTightList = false)
|
||||
{
|
||||
return $this->renderBlock($block, $inTightList);
|
||||
}
|
||||
}
|
||||
|
@ -1,831 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
|
||||
/**
|
||||
* Parses inline elements
|
||||
*/
|
||||
class CommonMark_InlineParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $subject;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $labelNestLevel = 0; // Used by parseLinkLabel method
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $pos = 0;
|
||||
|
||||
/**
|
||||
* @var ReferenceMap
|
||||
*/
|
||||
protected $refmap;
|
||||
|
||||
/**
|
||||
* @var RegexHelper
|
||||
*/
|
||||
protected $regexHelper;
|
||||
|
||||
/**
|
||||
* Constrcutor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->refmap = new CommonMark_Reference_ReferenceMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* If re matches at current position in the subject, advance
|
||||
* position in subject and return the match; otherwise return null
|
||||
* @param string $re
|
||||
*
|
||||
* @return string|null The match (if found); null otherwise
|
||||
*/
|
||||
protected function match($re)
|
||||
{
|
||||
$matches = array();
|
||||
$subject = substr($this->subject, $this->pos);
|
||||
if (!preg_match($re, $subject, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// [0][0] contains the matched text
|
||||
// [0][1] contains the index of that match
|
||||
$this->pos += $matches[0][1] + strlen($matches[0][0]);
|
||||
|
||||
return $matches[0][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the character at the current subject position, or null if
|
||||
* there are no more characters
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function peek()
|
||||
{
|
||||
$ch = substr($this->subject, $this->pos, 1);
|
||||
return false !== $ch && strlen($ch) > 0 ? $ch : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse zero or more space characters, including at most one newline
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function spnl()
|
||||
{
|
||||
$this->match('/^ *(?:\n *)?/');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// All of the parsers below try to match something at the current position
|
||||
// in the subject. If they succeed in matching anything, they
|
||||
// push an inline element onto the 'inlines' list. They return the
|
||||
// number of characters parsed (possibly 0).
|
||||
|
||||
/**
|
||||
* Attempt to parse backticks, adding either a backtick code span or a
|
||||
* literal sequence of backticks to the 'inlines' list.
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int Number of characters parsed
|
||||
*/
|
||||
protected function parseBackticks(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
$startpos = $this->pos;
|
||||
$ticks = $this->match('/^`+/');
|
||||
if (!$ticks) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$afterOpenTicks = $this->pos;
|
||||
$foundCode = false;
|
||||
$match = null;
|
||||
while (!$foundCode && ($match = $this->match('/`+/m'))) {
|
||||
if ($match == $ticks) {
|
||||
$c = substr($this->subject, $afterOpenTicks, $this->pos - $afterOpenTicks - strlen($ticks));
|
||||
$c = preg_replace('/[ \n]+/', ' ', $c);
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createCode(trim($c)));
|
||||
|
||||
return ($this->pos - $startpos);
|
||||
}
|
||||
}
|
||||
|
||||
// If we go here, we didn't match a closing backtick sequence
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createString($ticks));
|
||||
$this->pos = $afterOpenTicks;
|
||||
|
||||
return ($this->pos - $startpos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a backslash-escaped special character, adding either the escaped
|
||||
* character, a hard line break (if the backslash is followed by a newline),
|
||||
* or a literal backslash to the 'inlines' list.
|
||||
*
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseEscaped(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
$subject = $this->subject;
|
||||
$pos = $this->pos;
|
||||
if ($subject[$pos] === '\\') {
|
||||
if (isset($subject[$pos + 1]) && $subject[$pos + 1] === "\n") {
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createHardbreak());
|
||||
$this->pos = $this->pos + 2;
|
||||
|
||||
return 2;
|
||||
} elseif (isset($subject[$pos + 1]) && preg_match(
|
||||
'/' . CommonMark_Util_RegexHelper::REGEX_ESCAPABLE . '/',
|
||||
$subject[$pos + 1]
|
||||
)
|
||||
) {
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createString($subject[$pos + 1]));
|
||||
$this->pos = $this->pos + 2;
|
||||
|
||||
return 2;
|
||||
} else {
|
||||
$this->pos++;
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createString('\\'));
|
||||
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse an autolink (URL or email in pointy brackets)
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseAutolink(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
$emailRegex = '/^<([a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/';
|
||||
$otherLinkRegex = '/^<(?:coap|doi|javascript|aaa|aaas|about|acap|cap|cid|crid|data|dav|dict|dns|file|ftp|geo|go|gopher|h323|http|https|iax|icap|im|imap|info|ipp|iris|iris.beep|iris.xpc|iris.xpcs|iris.lwz|ldap|mailto|mid|msrp|msrps|mtqp|mupdate|news|nfs|ni|nih|nntp|opaquelocktoken|pop|pres|rtsp|service|session|shttp|sieve|sip|sips|sms|snmp|soap.beep|soap.beeps|tag|tel|telnet|tftp|thismessage|tn3270|tip|tv|urn|vemmi|ws|wss|xcon|xcon-userid|xmlrpc.beep|xmlrpc.beeps|xmpp|z39.50r|z39.50s|adiumxtra|afp|afs|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|chrome|chrome-extension|com-eventbrite-attendee|content|cvs|dlna-playsingle|dlna-playcontainer|dtn|dvb|ed2k|facetime|feed|finger|fish|gg|git|gizmoproject|gtalk|hcp|icon|ipn|irc|irc6|ircs|itms|jar|jms|keyparc|lastfm|ldaps|magnet|maps|market|message|mms|ms-help|msnim|mumble|mvn|notes|oid|palm|paparazzi|platform|proxy|psyc|query|res|resource|rmi|rsync|rtmp|secondlife|sftp|sgn|skype|smb|soldat|spotify|ssh|steam|svn|teamspeak|things|udp|unreal|ut2004|ventrilo|view-source|webcal|wtai|wyciwyg|xfire|xri|ymsgr):[^<>\x00-\x20]*>/i';
|
||||
|
||||
if ($m = $this->match($emailRegex)) {
|
||||
$email = substr($m, 1, -1);
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createLink('mailto:' . $email, $email));
|
||||
|
||||
return strlen($m);
|
||||
} elseif ($m = $this->match($otherLinkRegex)) {
|
||||
$dest = substr($m, 1, -1);
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createLink($dest, $dest));
|
||||
|
||||
return strlen($m);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse a raw HTML tag
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseHtmlTag(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
if ($m = $this->match(CommonMark_Util_RegexHelper::getInstance()->getHtmlTagRegex())) {
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createHtml($m));
|
||||
|
||||
return strlen($m);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a sequence of characters == c, and return information about
|
||||
* the number of delimiters and whether they are positioned such that
|
||||
* they can open and/or close emphasis or strong emphasis. A utility
|
||||
* function for strong/emph parsing.
|
||||
*
|
||||
* @param string $char
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function scanDelims($char)
|
||||
{
|
||||
$numDelims = 0;
|
||||
$startPos = $this->pos;
|
||||
|
||||
$charBefore = $this->pos === 0 ? "\n" : $this->subject[$this->pos - 1];
|
||||
|
||||
while ($this->peek() === $char) {
|
||||
$numDelims++;
|
||||
$this->pos++;
|
||||
}
|
||||
|
||||
$peek = $this->peek();
|
||||
$charAfter = $peek ? $peek : "\n";
|
||||
|
||||
$canOpen = $numDelims > 0 && $numDelims <= 3 && !preg_match('/\s/', $charAfter);
|
||||
$canClose = $numDelims > 0 && $numDelims <= 3 && !preg_match('/\s/', $charBefore);
|
||||
if ($char === '_') {
|
||||
$canOpen = $canOpen && !preg_match('/[a-z0-9]/i', $charBefore);
|
||||
$canClose = $canClose && !preg_match('/[a-z0-9]/i', $charAfter);
|
||||
}
|
||||
|
||||
$this->pos = $startPos;
|
||||
|
||||
return compact('numDelims', 'canOpen', 'canClose');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseEmphasis(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
$startPos = $this->pos;
|
||||
$firstClose = 0;
|
||||
$nxt = $this->peek();
|
||||
if ($nxt == '*' || $nxt == '_') {
|
||||
$c = $nxt;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get opening delimiters
|
||||
$res = $this->scanDelims($c);
|
||||
$numDelims = $res['numDelims'];
|
||||
$this->pos += $numDelims;
|
||||
|
||||
// We provisionally add a literal string. If we match appropriate
|
||||
// closing delimiters, we'll change this to Strong or Emph.
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createString(substr($this->subject, $this->pos - $numDelims, $numDelims)));
|
||||
// Record the position of this opening delimiter:
|
||||
$delimPos = $inlines->count() - 1;
|
||||
|
||||
if (!$res['canOpen'] || $numDelims === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$firstCloseDelims = 0;
|
||||
switch ($numDelims) {
|
||||
case 1: // we started with * or _
|
||||
while (true) {
|
||||
$res = $this->scanDelims($c);
|
||||
if ($res['numDelims'] >= 1 && $res['canClose']) {
|
||||
$this->pos += 1;
|
||||
// Convert the inline at delimpos, currently a string with the delim,
|
||||
// into an Emph whose contents are the succeeding inlines
|
||||
$inlines->get($delimPos)->setType(CommonMark_Element_InlineElement::TYPE_EMPH);
|
||||
$inlines->get($delimPos)->setContents($inlines->slice($delimPos + 1));
|
||||
$inlines->splice($delimPos + 1);
|
||||
break;
|
||||
} else {
|
||||
if ($this->parseInline($inlines) === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($this->pos - $startPos);
|
||||
|
||||
case 2: // We started with ** or __
|
||||
while (true) {
|
||||
$res = $this->scanDelims($c);
|
||||
if ($res['numDelims'] >= 2 && $res['canClose']) {
|
||||
$this->pos += 2;
|
||||
$inlines->get($delimPos)->setType(CommonMark_Element_InlineElement::TYPE_STRONG);
|
||||
$inlines->get($delimPos)->setContents($inlines->slice($delimPos + 1));
|
||||
$inlines->splice($delimPos + 1);
|
||||
break;
|
||||
} else {
|
||||
if ($this->parseInline($inlines) === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($this->pos - $startPos);
|
||||
|
||||
case 3: // We started with *** or ___
|
||||
while (true) {
|
||||
$res = $this->scanDelims($c);
|
||||
if ($res['numDelims'] >= 1 && $res['numDelims'] <= 3 && $res['canClose'] && $res['numDelims'] != $firstCloseDelims) {
|
||||
if ($firstCloseDelims === 1 && $numDelims > 2) {
|
||||
$res['numDelims'] = 2;
|
||||
} elseif ($firstCloseDelims === 2) {
|
||||
$res['numDelims'] = 1;
|
||||
} elseif ($res['numDelims'] === 3) {
|
||||
// If we opened with ***, then we interpret *** as ** followed by *
|
||||
// giving us <strong><em>
|
||||
$res['numDelims'] = 1;
|
||||
}
|
||||
|
||||
$this->pos += $res['numDelims'];
|
||||
|
||||
if ($firstClose > 0) { // if we've already passed the first closer:
|
||||
$targetInline = $inlines->get($delimPos);
|
||||
if ($firstCloseDelims === 1) {
|
||||
$targetInline->setType(CommonMark_Element_InlineElement::TYPE_STRONG);
|
||||
$targetInline->setContents(
|
||||
array(
|
||||
CommonMark_Element_InlineCreator::createEmph(
|
||||
$inlines->slice($delimPos + 1, $firstClose - $delimPos - 1)
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$targetInline->setType(CommonMark_Element_InlineElement::TYPE_EMPH);
|
||||
$targetInline->setContents(
|
||||
array(
|
||||
CommonMark_Element_InlineCreator::createStrong(
|
||||
$inlines->slice($delimPos + 1, $firstClose - $delimPos - 1)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$targetInline->setContents($targetInline->getContents() + $inlines->slice($firstClose + 1));
|
||||
$inlines->splice($delimPos + 1);
|
||||
break;
|
||||
} else {
|
||||
// this is the first closer; for now, add literal string;
|
||||
// we'll change this when he hit the second closer
|
||||
$str = substr($this->subject, $this->pos - $res['numDelims'], $this->pos);
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createString($str));
|
||||
$firstClose = $inlines->count() - 1;
|
||||
$firstCloseDelims = $res['numDelims'];
|
||||
}
|
||||
} else {
|
||||
// Parse another inline element, til we hit the end
|
||||
if ($this->parseInline($inlines) === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($this->pos - $startPos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse link title (sans quotes)
|
||||
*
|
||||
* @return null|string The string, or null if no match
|
||||
*/
|
||||
protected function parseLinkTitle()
|
||||
{
|
||||
if ($title = $this->match(CommonMark_Util_RegexHelper::getInstance()->getLinkTitleRegex())) {
|
||||
// Chop off quotes from title and unescape
|
||||
return CommonMark_Util_RegexHelper::unescape(substr($title, 1, strlen($title) - 2));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse link destination
|
||||
*
|
||||
* @return null|string The string, or null if no match
|
||||
*/
|
||||
protected function parseLinkDestination()
|
||||
{
|
||||
if ($res = $this->match(CommonMark_Util_RegexHelper::getInstance()->getLinkDestinationBracesRegex())) {
|
||||
// Chop off surrounding <..>:
|
||||
return CommonMark_Util_RegexHelper::unescape(substr($res, 1, strlen($res) - 2));
|
||||
} else {
|
||||
$res = $this->match(CommonMark_Util_RegexHelper::getInstance()->getLinkDestinationRegex());
|
||||
if ($res !== null) {
|
||||
return CommonMark_Util_RegexHelper::unescape($res);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
protected function parseLinkLabel()
|
||||
{
|
||||
if ($this->peek() != '[') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$startPos = $this->pos;
|
||||
$nestLevel = 0;
|
||||
if ($this->labelNestLevel > 0) {
|
||||
// If we've already checked to the end of this subject
|
||||
// for a label, even with a different starting [, we
|
||||
// know we won't find one here and we can just return.
|
||||
// This avoids lots of backtracking.
|
||||
// Note: nest level 1 would be: [foo [bar]
|
||||
// nest level 2 would be: [foo [bar [baz]
|
||||
$this->labelNestLevel--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->pos++; // Advance past [
|
||||
while (($c = $this->peek()) !== null && ($c != ']' || $nestLevel > 0)) {
|
||||
switch ($c) {
|
||||
case '`':
|
||||
$this->parseBackticks(new CommonMark_Util_ArrayCollection());
|
||||
break;
|
||||
case '<':
|
||||
$this->parseAutolink(new CommonMark_Util_ArrayCollection()) || $this->parseHtmlTag(
|
||||
new CommonMark_Util_ArrayCollection()
|
||||
) || $this->parseString(new CommonMark_Util_ArrayCollection()); // TODO: Does PHP support this use of "||"?
|
||||
break;
|
||||
case '[': // nested []
|
||||
$nestLevel++;
|
||||
$this->pos++;
|
||||
break;
|
||||
case ']': //nested []
|
||||
$nestLevel--;
|
||||
$this->pos++;
|
||||
break;
|
||||
case '\\':
|
||||
$this->parseEscaped(new CommonMark_Util_ArrayCollection());
|
||||
break;
|
||||
default:
|
||||
$this->parseString(new CommonMark_Util_ArrayCollection());
|
||||
}
|
||||
}
|
||||
|
||||
if ($c === ']') {
|
||||
$this->labelNestLevel = 0;
|
||||
$this->pos++; // advance past ]
|
||||
|
||||
return $this->pos - $startPos;
|
||||
} else {
|
||||
if ($c === null) {
|
||||
$this->labelNestLevel = $nestLevel;
|
||||
}
|
||||
|
||||
$this->pos = $startPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse raw link label, including surrounding [], and return
|
||||
* inline contents.
|
||||
*
|
||||
* @param string $s
|
||||
*
|
||||
* @return ArrayCollection|InlineElementInterface[] Inline contents
|
||||
*/
|
||||
private function parseRawLabel($s)
|
||||
{
|
||||
// note: parse without a refmap; we don't want links to resolve
|
||||
// in nested brackets!
|
||||
$parser = new self();
|
||||
$substring = substr($s, 1, strlen($s) - 2);
|
||||
|
||||
return $parser->parse($substring, new CommonMark_Reference_ReferenceMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse a link. If successful, add the link to inlines.
|
||||
* @param ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseLink(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
$startPos = $this->pos;
|
||||
$n = $this->parseLinkLabel();
|
||||
if ($n === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$rawLabel = substr($this->subject, $startPos, $n);
|
||||
|
||||
// if we got this far, we've parsed a label.
|
||||
// Try to parse an explicit link: [label](url "title")
|
||||
if ($this->peek() == '(') {
|
||||
$this->pos++;
|
||||
if ($this->spnl() &&
|
||||
(($dest = $this->parseLinkDestination()) !== null) &&
|
||||
$this->spnl()
|
||||
) {
|
||||
// make sure there's a space before the title:
|
||||
if (preg_match('/^\\s/', $this->subject[$this->pos - 1])) {
|
||||
$title = $this->parseLinkTitle();
|
||||
$title = $title ? $title : '';
|
||||
} else {
|
||||
$title = null;
|
||||
}
|
||||
|
||||
if ($this->spnl() && $this->match('/^\\)/')) {
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createLink($dest, $this->parseRawLabel($rawLabel), $title));
|
||||
|
||||
return $this->pos - $startPos;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pos = $startPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we're here, it wasn't an explicit link. Try to parse a reference link.
|
||||
// first, see if there's another label
|
||||
$savePos = $this->pos;
|
||||
$this->spnl();
|
||||
$beforeLabel = $this->pos;
|
||||
$n = $this->parseLinkLabel();
|
||||
if ($n == 2) {
|
||||
// empty second label
|
||||
$refLabel = $rawLabel;
|
||||
} elseif ($n > 0) {
|
||||
$refLabel = substr($this->subject, $beforeLabel, $n);
|
||||
} else {
|
||||
$this->pos = $savePos;
|
||||
$refLabel = $rawLabel;
|
||||
}
|
||||
|
||||
// Lookup rawLabel in refmap
|
||||
if ($link = $this->refmap->getReference($refLabel)) {
|
||||
$inlines->add(
|
||||
CommonMark_Element_InlineCreator::createLink($link->getDestination(), $this->parseRawLabel($rawLabel), $link->getTitle())
|
||||
);
|
||||
|
||||
return $this->pos - $startPos;
|
||||
}
|
||||
|
||||
// Nothing worked, rewind:
|
||||
$this->pos = $startPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse an entity, adding to inlines if successful
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseEntity(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
if ($m = $this->match('/^&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});/i')) {
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createEntity($m));
|
||||
|
||||
return strlen($m);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a run of ordinary characters, or a single character with
|
||||
* a special meaning in markdown, as a plain string, adding to inlines.
|
||||
*
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseString(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
if ($m = $this->match(CommonMark_Util_RegexHelper::getInstance()->getMainRegex())) {
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createString($m));
|
||||
|
||||
return strlen($m);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a newline. If it was preceded by two spaces, return a hard
|
||||
* line break; otherwise a soft line break.
|
||||
*
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseNewline(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
if ($this->peek() == "\n") {
|
||||
$this->pos++;
|
||||
$last = $inlines->last();
|
||||
if ($last && $last->getType() == CommonMark_Element_InlineElement::TYPE_STRING && substr($last->getContents(), -2) == ' ') {
|
||||
$last->setContents(rtrim($last->getContents(), ' '));
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createHardbreak());
|
||||
} else {
|
||||
if ($last && $last->getType() == CommonMark_Element_InlineElement::TYPE_STRING && substr(
|
||||
$last->getContents(),
|
||||
-1
|
||||
) == ' '
|
||||
) {
|
||||
$last->setContents(substr($last->getContents(), 0, -1));
|
||||
}
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createSoftbreak());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function parseImage(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
if ($this->match('/^!/')) {
|
||||
$n = $this->parseLink($inlines);
|
||||
if ($n === 0) {
|
||||
$inlines->add(CommonMark_Element_InlineCreator::createString('!'));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @var InlineElementInterface $last */
|
||||
$last = $inlines->last();
|
||||
|
||||
if ($last && $last->getType() == CommonMark_Element_InlineElement::TYPE_LINK) {
|
||||
$last->setType(CommonMark_Element_InlineElement::TYPE_IMAGE);
|
||||
|
||||
return $n + 1;
|
||||
} else {
|
||||
// This shouldn't happen
|
||||
throw new RuntimeException('Unknown error occurred while attempting to parse an image');
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the next inline element in subject, advancing subject position
|
||||
* and adding the result to 'inlines'.
|
||||
*
|
||||
* @param \ColinODell\CommonMark\Util\ArrayCollection $inlines
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function parseInline(CommonMark_Util_ArrayCollection $inlines)
|
||||
{
|
||||
$c = $this->peek();
|
||||
$res = null;
|
||||
|
||||
switch ($c) {
|
||||
case "\n":
|
||||
$res = $this->parseNewline($inlines);
|
||||
break;
|
||||
case '\\':
|
||||
$res = $this->parseEscaped($inlines);
|
||||
break;
|
||||
case '`':
|
||||
$res = $this->parseBackticks($inlines);
|
||||
break;
|
||||
case '*':
|
||||
case '_':
|
||||
$res = $this->parseEmphasis($inlines);
|
||||
break;
|
||||
case '[':
|
||||
$res = $this->parseLink($inlines);
|
||||
break;
|
||||
case '!':
|
||||
$res = $this->parseImage($inlines);
|
||||
break;
|
||||
case '<':
|
||||
$res = $this->parseAutolink($inlines);
|
||||
$res = $res ? $res : $this->parseHtmlTag($inlines);
|
||||
break;
|
||||
case '&':
|
||||
$res = $this->parseEntity($inlines);
|
||||
break;
|
||||
default:
|
||||
// Nothing
|
||||
}
|
||||
|
||||
return $res ? $res : $this->parseString($inlines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse s as a list of inlines, using refmap to resolve references.
|
||||
*
|
||||
* @param string $s
|
||||
* @param ReferenceMap $refMap
|
||||
*
|
||||
* @return ArrayCollection|InlineElementInterface[]
|
||||
*/
|
||||
protected function parseInlines($s, CommonMark_Reference_ReferenceMap $refMap)
|
||||
{
|
||||
$this->subject = $s;
|
||||
$this->pos = 0;
|
||||
$this->refmap = $refMap;
|
||||
$inlines = new CommonMark_Util_ArrayCollection();
|
||||
while ($this->parseInline($inlines)) {
|
||||
;
|
||||
}
|
||||
|
||||
return $inlines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $s
|
||||
* @param ReferenceMap $refMap
|
||||
*
|
||||
* @return ArrayCollection|Element\InlineElementInterface[]
|
||||
*/
|
||||
public function parse($s, CommonMark_Reference_ReferenceMap $refMap)
|
||||
{
|
||||
return $this->parseInlines($s, $refMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse a link reference, modifying refmap.
|
||||
* @param string $s
|
||||
* @param ReferenceMap $refMap
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function parseReference($s, CommonMark_Reference_ReferenceMap $refMap)
|
||||
{
|
||||
$this->subject = $s;
|
||||
$this->pos = 0;
|
||||
$startPos = $this->pos;
|
||||
|
||||
// label:
|
||||
$matchChars = $this->parseLinkLabel();
|
||||
if ($matchChars === 0) {
|
||||
return 0;
|
||||
} else {
|
||||
$label = substr($this->subject, 0, $matchChars);
|
||||
}
|
||||
|
||||
// colon:
|
||||
if ($this->peek() === ':') {
|
||||
$this->pos++;
|
||||
} else {
|
||||
$this->pos = $startPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// link url
|
||||
$this->spnl();
|
||||
|
||||
$destination = $this->parseLinkDestination();
|
||||
if ($destination === null || strlen($destination) === 0) {
|
||||
$this->pos = $startPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$beforeTitle = $this->pos;
|
||||
$this->spnl();
|
||||
$title = $this->parseLinkTitle();
|
||||
if ($title === null) {
|
||||
$title = '';
|
||||
// rewind before spaces
|
||||
$this->pos = $beforeTitle;
|
||||
}
|
||||
|
||||
// make sure we're at line end:
|
||||
if ($this->match('/^ *(?:\n|$)/') === null) {
|
||||
$this->pos = $startPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$refMap->contains($label)) {
|
||||
$refMap->addReference(new CommonMark_Reference_Reference($label, $destination, $title));
|
||||
}
|
||||
|
||||
return $this->pos - $startPos;
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
|
||||
/**
|
||||
* Link reference
|
||||
*/
|
||||
class CommonMark_Reference_Reference
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $destination;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $label
|
||||
* @param string $destination
|
||||
* @param string $title
|
||||
*/
|
||||
public function __construct($label, $destination, $title)
|
||||
{
|
||||
$this->label = self::normalizeReference($label);
|
||||
$this->destination = $destination;
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDestination()
|
||||
{
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize reference label
|
||||
*
|
||||
* This enables case-insensitive label matching
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function normalizeReference($string)
|
||||
{
|
||||
// Collapse internal whitespace to single space and remove
|
||||
// leading/trailing whitespace
|
||||
$string = preg_replace('/\s+/', '', trim($string));
|
||||
|
||||
return Typecho_Common::strToUpper($string, 'UTF-8');
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
/**
|
||||
* A collection of references, indexed by label
|
||||
*/
|
||||
class CommonMark_Reference_ReferenceMap
|
||||
{
|
||||
/**
|
||||
* @var Reference[]
|
||||
*/
|
||||
protected $references = array();
|
||||
|
||||
/**
|
||||
* @param Reference $reference
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addReference(CommonMark_Reference_Reference $reference)
|
||||
{
|
||||
$key = CommonMark_Reference_Reference::normalizeReference($reference->getLabel());
|
||||
$this->references[$key] = $reference;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function contains($label)
|
||||
{
|
||||
$label = CommonMark_Reference_Reference::normalizeReference($label);
|
||||
|
||||
return isset($this->references[$label]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label
|
||||
*
|
||||
* @return Reference|null
|
||||
*/
|
||||
public function getReference($label)
|
||||
{
|
||||
$label = CommonMark_Reference_Reference::normalizeReference($label);
|
||||
|
||||
if (isset($this->references[$label])) {
|
||||
return $this->references[$label];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
/**
|
||||
* Array collection
|
||||
*
|
||||
* Provides a wrapper around a standard PHP array.
|
||||
*/
|
||||
class CommonMark_Util_ArrayCollection implements IteratorAggregate, Countable, ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $elements;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param array $elements
|
||||
*/
|
||||
public function __construct(array $elements = array())
|
||||
{
|
||||
$this->elements = $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
return reset($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
return end($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* @return \Traversable
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $element
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function add($element)
|
||||
{
|
||||
$this->elements[] = $element;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->elements[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return isset($this->elements[$key]) ? $this->elements[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
if (!isset($this->elements[$key]) && !array_key_exists($key, $this->elements)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$removed = $this->elements[$key];
|
||||
unset($this->elements[$key]);
|
||||
|
||||
return $removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function containsKey($key)
|
||||
{
|
||||
return isset($this->elements[$key]) || array_key_exists($key, $this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
*
|
||||
* @return int The count as an integer.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether an offset exists
|
||||
*
|
||||
* @param mixed $offset An offset to check for.
|
||||
*
|
||||
* @return boolean true on success or false on failure.
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->containsKey($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @param mixed $offset The offset to retrieve.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @param mixed $offset The offset to assign the value to.
|
||||
* @param mixed $value The value to set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (!isset($offset)) {
|
||||
$this->add($value);
|
||||
} else {
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @param mixed $offset The offset to unset.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->remove($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a subset of the array
|
||||
* @param int $offset
|
||||
* @param int|null $length
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
return array_slice($this->elements, $offset, $length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a subset of the array
|
||||
*
|
||||
* The removed part will be returned
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int|null $length
|
||||
* @param array $replacement
|
||||
*
|
||||
* @return array The removed subset
|
||||
*/
|
||||
public function splice($offset, $length = null, $replacement = array())
|
||||
{
|
||||
if ($length === null) {
|
||||
$length = count($this->elements);
|
||||
}
|
||||
|
||||
return array_splice($this->elements, $offset, $length, $replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the commonmark-php package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on stmd.js
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
/**
|
||||
* Provides regular expressions and utilties for parsing Markdown
|
||||
*
|
||||
* Singletons are generally bad, but it allows us to build the regexes once (and only once).
|
||||
*/
|
||||
class CommonMark_Util_RegexHelper
|
||||
{
|
||||
const ESCAPABLE = 0;
|
||||
const ESCAPED_CHAR = 1;
|
||||
const IN_DOUBLE_QUOTES = 2;
|
||||
const IN_SINGLE_QUOTES = 3;
|
||||
const IN_PARENS = 4;
|
||||
const REG_CHAR = 5;
|
||||
const IN_PARENS_NOSP = 6;
|
||||
const TAGNAME = 7;
|
||||
const BLOCKTAGNAME = 8;
|
||||
const ATTRIBUTENAME = 9;
|
||||
const UNQUOTEDVALUE = 10;
|
||||
const SINGLEQUOTEDVALUE = 11;
|
||||
const DOUBLEQUOTEDVALUE = 12;
|
||||
const ATTRIBUTEVALUE = 13;
|
||||
const ATTRIBUTEVALUESPEC = 14;
|
||||
const ATTRIBUTE = 15;
|
||||
const OPENTAG = 16;
|
||||
const CLOSETAG = 17;
|
||||
const OPENBLOCKTAG = 18;
|
||||
const CLOSEBLOCKTAG = 19;
|
||||
const HTMLCOMMENT = 20;
|
||||
const PROCESSINGINSTRUCTION = 21;
|
||||
const DECLARATION = 22;
|
||||
const CDATA = 23;
|
||||
const HTMLTAG = 24;
|
||||
const HTMLBLOCKOPEN = 25;
|
||||
const LINK_TITLE = 26;
|
||||
|
||||
const REGEX_ESCAPABLE = '[!"#$%&\'()*+,.\/:;<=>?@[\\\\\]^_`{|}~-]';
|
||||
|
||||
protected $regex = array();
|
||||
|
||||
static protected $instance;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
$this->buildRegexPatterns();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RegexHelper
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new CommonMark_Util_RegexHelper();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the regular expressions required to parse Markdown
|
||||
*
|
||||
* We could hard-code them all as pre-built constants, but that would be more difficult to manage.
|
||||
*/
|
||||
protected function buildRegexPatterns()
|
||||
{
|
||||
$regex = array();
|
||||
$regex[self::ESCAPABLE] = self::REGEX_ESCAPABLE;
|
||||
$regex[self::ESCAPED_CHAR] = '\\\\' . $regex[self::ESCAPABLE];
|
||||
$regex[self::IN_DOUBLE_QUOTES] = '"(' . $regex[self::ESCAPED_CHAR] . '|[^"\x00])*"';
|
||||
$regex[self::IN_SINGLE_QUOTES] = '\'(' . $regex[self::ESCAPED_CHAR] . '|[^\'\x00])*\'';
|
||||
$regex[self::IN_PARENS] = '\\((' . $regex[self::ESCAPED_CHAR] . '|[^)\x00])*\\)';
|
||||
$regex[self::REG_CHAR] = '[^\\\\()\x00-\x20]';
|
||||
$regex[self::IN_PARENS_NOSP] = '\((' . $regex[self::REG_CHAR] . '|' . $regex[self::ESCAPED_CHAR] . ')*\)';
|
||||
$regex[self::TAGNAME] = '[A-Za-z][A-Za-z0-9]*';
|
||||
$regex[self::BLOCKTAGNAME] = '(?:article|header|aside|hgroup|iframe|blockquote|hr|body|li|map|button|object|canvas|ol|caption|output|col|p|colgroup|pre|dd|progress|div|section|dl|table|td|dt|tbody|embed|textarea|fieldset|tfoot|figcaption|th|figure|thead|footer|footer|tr|form|ul|h1|h2|h3|h4|h5|h6|video|script|style)';
|
||||
$regex[self::ATTRIBUTENAME] = '[a-zA-Z_:][a-zA-Z0-9:._-]*';
|
||||
$regex[self::UNQUOTEDVALUE] = '[^"\'=<>`\x00-\x20]+';
|
||||
$regex[self::SINGLEQUOTEDVALUE] = '\'[^\']*\'';
|
||||
$regex[self::DOUBLEQUOTEDVALUE] = '"[^"]*"';
|
||||
$regex[self::ATTRIBUTEVALUE] = '(?:' . $regex[self::UNQUOTEDVALUE] . '|' . $regex[self::SINGLEQUOTEDVALUE] . '|' . $regex[self::DOUBLEQUOTEDVALUE] . ')';
|
||||
$regex[self::ATTRIBUTEVALUESPEC] = '(?:' . '\s*=' . '\s*' . $regex[self::ATTRIBUTEVALUE] . ')';
|
||||
$regex[self::ATTRIBUTE] = '(?:' . '\s+' . $regex[self::ATTRIBUTENAME] . $regex[self::ATTRIBUTEVALUESPEC] . '?)';
|
||||
$regex[self::OPENTAG] = '<' . $regex[self::TAGNAME] . $regex[self::ATTRIBUTE] . '*' . '\s*\/?>';
|
||||
$regex[self::CLOSETAG] = '<\/' . $regex[self::TAGNAME] . '\s*[>]';
|
||||
$regex[self::OPENBLOCKTAG] = '<' . $regex[self::BLOCKTAGNAME] . $regex[self::ATTRIBUTE] . '*' . '\s*\/?>';
|
||||
$regex[self::CLOSEBLOCKTAG] = '<\/' . $regex[self::BLOCKTAGNAME] . '\s*[>]';
|
||||
$regex[self::HTMLCOMMENT] = '<!--([^-]+|[-][^-]+)*-->';
|
||||
$regex[self::PROCESSINGINSTRUCTION] = '[<][?].*?[?][>]';
|
||||
$regex[self::DECLARATION] = '<![A-Z]+' . '\s+[^>]*>';
|
||||
$regex[self::CDATA] = '<!\[CDATA\[([^\]]+|\][^\]]|\]\][^>])*\]\]>';
|
||||
$regex[self::HTMLTAG] = '(?:' . $regex[self::OPENTAG] . '|' . $regex[self::CLOSETAG] . '|' . $regex[self::HTMLCOMMENT] . '|' .
|
||||
$regex[self::PROCESSINGINSTRUCTION] . '|' . $regex[self::DECLARATION] . '|' . $regex[self::CDATA] . ')';
|
||||
$regex[self::HTMLBLOCKOPEN] = '<(?:' . $regex[self::BLOCKTAGNAME] . '[\s\/>]' . '|' .
|
||||
'\/' . $regex[self::BLOCKTAGNAME] . '[\s>]' . '|' . '[?!])';
|
||||
$regex[self::LINK_TITLE] = '^(?:"(' . $regex[self::ESCAPED_CHAR] . '|[^"\x00])*"' .
|
||||
'|' . '\'(' . $regex[self::ESCAPED_CHAR] . '|[^\'\x00])*\'' .
|
||||
'|' . '\((' . $regex[self::ESCAPED_CHAR] . '|[^)\x00])*\))';
|
||||
|
||||
$this->regex = $regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a partial regex
|
||||
*
|
||||
* It'll need to be wrapped with /.../ before use
|
||||
* @param int $const
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartialRegex($const)
|
||||
{
|
||||
return $this->regex[$const];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHtmlTagRegex()
|
||||
{
|
||||
return '/^' . $this->regex[self::HTMLTAG] . '/i';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHtmlBlockOpenRegex()
|
||||
{
|
||||
return '/^' . $this->regex[self::HTMLBLOCKOPEN] . '/i';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLinkTitleRegex()
|
||||
{
|
||||
return '/' . $this->regex[self::LINK_TITLE] . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLinkDestinationRegex()
|
||||
{
|
||||
return '/^' . '(?:' . $this->regex[self::REG_CHAR] . '+|' . $this->regex[self::ESCAPED_CHAR] . '|' . $this->regex[self::IN_PARENS_NOSP] . ')*' . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLinkDestinationBracesRegex()
|
||||
{
|
||||
return '/^(?:' . '[<](?:[^<>\\n\\\\\\x00]' . '|' . $this->regex[self::ESCAPED_CHAR] . '|' . '\\\\)*[>]' . ')/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHRuleRegex()
|
||||
{
|
||||
return '/^(?:(?:\* *){3,}|(?:_ *){3,}|(?:- *){3,}) *$/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a character with a special meaning in markdown,
|
||||
* or a string of non-special characters.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMainRegex()
|
||||
{
|
||||
return '/^(?:[\n`\[\]\\\\!<&*_]|[^\n`\[\]\\\\!<&*_]+)/m';
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to match a regex in string s at offset offset
|
||||
* @param string $regex
|
||||
* @param string $string
|
||||
* @param int $offset
|
||||
*
|
||||
* @return int|null Index of match, or null
|
||||
*/
|
||||
public static function matchAt($regex, $string, $offset)
|
||||
{
|
||||
$matches = array();
|
||||
$string = substr($string, $offset);
|
||||
if (!preg_match($regex, $string, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $offset + $matches[0][1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional wrapper around preg_match_all
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param string $subject
|
||||
* @param int $offset
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public static function matchAll($pattern, $subject, $offset = 0)
|
||||
{
|
||||
$matches = array();
|
||||
$subject = substr($subject, $offset);
|
||||
preg_match_all($pattern, $subject, $matches, PREG_PATTERN_ORDER);
|
||||
|
||||
$fullMatches = reset($matches);
|
||||
if (empty($fullMatches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (count($fullMatches) == 1) {
|
||||
foreach ($matches as &$match) {
|
||||
$match = reset($match);
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($matches) ? $matches : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace backslash escapes with literal characters
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function unescape($string)
|
||||
{
|
||||
$allEscapedChar = '/\\\\(' . self::REGEX_ESCAPABLE . ')/';
|
||||
|
||||
return preg_replace($allEscapedChar, '$1', $string);
|
||||
}
|
||||
}
|
@ -1,32 +1,6 @@
|
||||
<?php
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
/**
|
||||
* 扩展htmlrender,使得其支持行内换行
|
||||
*
|
||||
* @package Markdown
|
||||
* @copyright Copyright (c) 2014 Typecho team (http://www.typecho.org)
|
||||
* @license GNU General Public License 2.0
|
||||
*/
|
||||
class HtmlRendererExtra extends CommonMark_HtmlRenderer
|
||||
{
|
||||
/**
|
||||
* renderInline
|
||||
*
|
||||
* @param CommonMark_Element_InlineElementInterface $inline
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function renderInline(CommonMark_Element_InlineElementInterface $inline)
|
||||
{
|
||||
if ($inline->getType() == CommonMark_Element_InlineElement::TYPE_SOFTBREAK) {
|
||||
$inline->setType(CommonMark_Element_InlineElement::TYPE_HARDBREAK);
|
||||
}
|
||||
|
||||
return parent::renderInline($inline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown解析
|
||||
*
|
||||
|
@ -167,6 +167,32 @@ class Typecho_Db
|
||||
return $this->_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择数据库
|
||||
*
|
||||
* @param int $op
|
||||
* @return Typecho_Db_Adapter
|
||||
*/
|
||||
public function selectDb($op)
|
||||
{
|
||||
if (!isset($this->_connectedPool[$op])) {
|
||||
if (empty($this->_pool[$op])) {
|
||||
/** Typecho_Db_Exception */
|
||||
throw new Typecho_Db_Exception('Missing Database Connection');
|
||||
}
|
||||
|
||||
//获取相应读或写服务器连接池中的一个
|
||||
$selectConnection = rand(0, count($this->_pool[$op]) - 1);
|
||||
//获取随机获得的连接池配置
|
||||
$selectConnectionConfig = $this->_config[$this->_pool[$op][$selectConnection]];
|
||||
$selectConnectionHandle = $this->_adapter->connect($selectConnectionConfig);
|
||||
$this->_connectedPool[$op] = &$selectConnectionHandle;
|
||||
|
||||
}
|
||||
|
||||
return $this->_connectedPool[$op];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SQL词法构建器实例化对象
|
||||
*
|
||||
@ -203,6 +229,17 @@ class Typecho_Db
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本
|
||||
*
|
||||
* @param int $op
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion($op = self::READ)
|
||||
{
|
||||
return $this->_adapter->getVersion($this->selectDb($op));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认数据库对象
|
||||
*
|
||||
@ -299,21 +336,7 @@ class Typecho_Db
|
||||
}
|
||||
|
||||
/** 选择连接池 */
|
||||
if (!isset($this->_connectedPool[$op])) {
|
||||
if (empty($this->_pool[$op])) {
|
||||
/** Typecho_Db_Exception */
|
||||
throw new Typecho_Db_Exception('Missing Database Connection');
|
||||
}
|
||||
|
||||
//获取相应读或写服务器连接池中的一个
|
||||
$selectConnection = rand(0, count($this->_pool[$op]) - 1);
|
||||
//获取随机获得的连接池配置
|
||||
$selectConnectionConfig = $this->_config[$this->_pool[$op][$selectConnection]];
|
||||
$selectConnectionHandle = $this->_adapter->connect($selectConnectionConfig);
|
||||
$this->_connectedPool[$op] = &$selectConnectionHandle;
|
||||
|
||||
}
|
||||
$handle = $this->_connectedPool[$op];
|
||||
$handle = $this->selectDb($op);
|
||||
|
||||
/** 提交查询 */
|
||||
$resource = $this->_adapter->query($query, $handle, $op, $action);
|
||||
|
@ -31,6 +31,14 @@ interface Typecho_Db_Adapter
|
||||
*/
|
||||
public function connect(Typecho_Config $config);
|
||||
|
||||
/**
|
||||
* 获取数据库版本
|
||||
*
|
||||
* @param mixed $handle
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion($handle);
|
||||
|
||||
/**
|
||||
* 执行数据库查询
|
||||
*
|
||||
|
@ -57,6 +57,17 @@ class Typecho_Db_Adapter_Mysql implements Typecho_Db_Adapter
|
||||
throw new Typecho_Db_Adapter_Exception(@mysql_error($this->_dbLink));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库版本
|
||||
*
|
||||
* @param mixed $handle
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion($handle)
|
||||
{
|
||||
return 'ext:mysql ' . mysql_get_server_info($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库查询
|
||||
*
|
||||
|
@ -61,6 +61,18 @@ abstract class Typecho_Db_Adapter_Pdo implements Typecho_Db_Adapter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库版本
|
||||
*
|
||||
* @param mixed $handle
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion($handle)
|
||||
{
|
||||
return 'pdo:' . $handle->getAttribute(PDO::ATTR_DRIVER_NAME)
|
||||
. ' ' . $handle->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据库
|
||||
*
|
||||
|
@ -62,6 +62,18 @@ class Typecho_Db_Adapter_Pgsql implements Typecho_Db_Adapter
|
||||
throw new Typecho_Db_Adapter_Exception(@pg_last_error($this->_dbLink));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库版本
|
||||
*
|
||||
* @param mixed $handle
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion($handle)
|
||||
{
|
||||
$version = pg_version($handle);
|
||||
return 'ext:pgsql ' . $version['server'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库查询
|
||||
*
|
||||
|
@ -51,6 +51,17 @@ class Typecho_Db_Adapter_SQLite implements Typecho_Db_Adapter
|
||||
throw new Typecho_Db_Adapter_Exception($error);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库版本
|
||||
*
|
||||
* @param mixed $handle
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion($handle)
|
||||
{
|
||||
return 'ext:sqlite ' . sqlite_libversion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库查询
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user