Initial Commit
Этот коммит содержится в:
32
src/HeaderTagInterpreter.php
Обычный файл
32
src/HeaderTagInterpreter.php
Обычный файл
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: casey
|
||||
* Date: 12/30/14
|
||||
* Time: 12:32 PM
|
||||
*/
|
||||
|
||||
namespace TOC;
|
||||
|
||||
/**
|
||||
* Interprets header tags
|
||||
*
|
||||
* @package TOC
|
||||
*/
|
||||
trait HeaderTagInterpreter
|
||||
{
|
||||
/**
|
||||
* Convert a topLevel and depth to H1..H6 tags array
|
||||
*
|
||||
* @param int $topLevel
|
||||
* @param int $depth
|
||||
* @return array Array of header tags; ex: ['h1', 'h2', 'h3']
|
||||
*/
|
||||
protected function determineHeaderTags($topLevel, $depth)
|
||||
{
|
||||
$desired = range((int) $topLevel, (int) $topLevel + ((int) $depth - 1));
|
||||
$allowed = [1, 2, 3, 4, 5, 6];
|
||||
|
||||
return array_map(function($v) { return 'h'.$v; }, array_intersect($desired, $allowed));
|
||||
}
|
||||
}
|
67
src/Sluggifier.php
Обычный файл
67
src/Sluggifier.php
Обычный файл
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: casey
|
||||
* Date: 12/30/14
|
||||
* Time: 12:48 PM
|
||||
*/
|
||||
|
||||
namespace TOC;
|
||||
|
||||
use Cocur\Slugify\Slugify;
|
||||
|
||||
/**
|
||||
* Sluggifier creates slugs text without repeating the same string twice per instance
|
||||
*
|
||||
* @package TOC
|
||||
*/
|
||||
class Sluggifier
|
||||
{
|
||||
/**
|
||||
* @var \Cocur\Slugify\Slugify
|
||||
*/
|
||||
private $slugify;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $used;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cocur\Slugify\Slugify $slugify
|
||||
*/
|
||||
public function __construct(Slugify $slugify = null)
|
||||
{
|
||||
$this->used = array();
|
||||
$this->slugify = $slugify ?: new Slugify();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Slugify
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public function slugify($text)
|
||||
{
|
||||
$slugged = $this->slugify->slugify($text);
|
||||
|
||||
$ct = 1;
|
||||
$orig = $slugged;
|
||||
while (in_array($slugged, $this->used)) {
|
||||
$slugged = $orig . '-' . $ct;
|
||||
$ct++;
|
||||
}
|
||||
|
||||
$this->used[] = $slugged;
|
||||
return $slugged;
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF: Sluggifier.php */
|
107
src/TocGenerator.php
Обычный файл
107
src/TocGenerator.php
Обычный файл
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
// Header here
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
namespace TOC;
|
||||
|
||||
use Sunra\PhpSimple\HtmlDomParser;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class TocGenerator
|
||||
*
|
||||
* @author Casey McLaughlin <caseyamcl@gmail.com>
|
||||
*/
|
||||
class TocGenerator
|
||||
{
|
||||
use HeaderTagInterpreter;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var \Sunra\PhpSimple\HtmlDomParser
|
||||
*/
|
||||
private $domParser;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Sunra\PhpSimple\HtmlDomParser $domParser
|
||||
*/
|
||||
public function __construct(HtmlDomParser $domParser = null)
|
||||
{
|
||||
$this->domParser = $domParser ?: new HtmlDomParser();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get Link Items
|
||||
*
|
||||
* @param string $markup Content to get items from
|
||||
* @param int $topLevel Top Header (1 through 6)
|
||||
* @param int $depth Depth (1 through 6)
|
||||
* @return array Array of items ['anchor' => 'display text', ...]
|
||||
*/
|
||||
public function getItems($markup, $topLevel = 1, $depth = 2)
|
||||
{
|
||||
// Empty? Do nothing.
|
||||
if (trim($markup) == '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Parse HTML
|
||||
$items = [];
|
||||
$tags = $this->determineHeaderTags($topLevel, $depth);
|
||||
$parsed = $this->domParser->str_get_html($markup);
|
||||
|
||||
// Runtime exception for bad code
|
||||
if ( ! $parsed) {
|
||||
throw new RuntimeException("Could not parse HTML");
|
||||
}
|
||||
|
||||
// Extract items
|
||||
foreach ($parsed->find(implode(', ', $tags)) as $tag) {
|
||||
|
||||
if ( ! $tag->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dispText = $tag->title ?: $tag->plaintext;
|
||||
$items[$tag->id] = $dispText;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get HTML Links in list form
|
||||
*
|
||||
* @param string $markup Content to get items from
|
||||
* @param int $topLevel Top Header (1 through 6)
|
||||
* @param int $depth Depth (1 through 6)
|
||||
* @return string HTML <LI> items
|
||||
*/
|
||||
public function getHtmlItems($markup, $topLevel = 1, $depth = 2, $titleTemplate = 'Go to %s')
|
||||
{
|
||||
$arr = [];
|
||||
|
||||
foreach ($this->getItems($markup, $topLevel, $depth) as $anchor => $displayText) {
|
||||
$arr[] = sprintf(
|
||||
"<li><a title='%s' href='#%s'>%s</a></li>",
|
||||
sprintf($titleTemplate, $displayText),
|
||||
$anchor,
|
||||
$displayText
|
||||
);
|
||||
}
|
||||
|
||||
return implode('', $arr);
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF: TocGenerator.php */
|
81
src/TocMarkupFixer.php
Обычный файл
81
src/TocMarkupFixer.php
Обычный файл
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: casey
|
||||
* Date: 12/30/14
|
||||
* Time: 12:20 PM
|
||||
*/
|
||||
|
||||
namespace TOC;
|
||||
|
||||
use RuntimeException;
|
||||
use Sunra\PhpSimple\HtmlDomParser;
|
||||
|
||||
/**
|
||||
* TOC Markup Fixer adds `id` attributes to all H1...H6 tags where they do not
|
||||
* already exist
|
||||
*
|
||||
* @author Casey McLaughlin <caseyamcl@gmail.com>
|
||||
*/
|
||||
class TocMarkupFixer
|
||||
{
|
||||
use HeaderTagInterpreter;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var HtmlDomParser
|
||||
*/
|
||||
private $domParser;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param HtmlDomParser $domParser
|
||||
*/
|
||||
public function __construct(HtmlDomParser $domParser = null)
|
||||
{
|
||||
$this->domParser = $domParser ?: new HtmlDomParser();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fix markup
|
||||
*
|
||||
* @param string $markup
|
||||
* @param int $topLevel
|
||||
* @param int $depth
|
||||
* @return string Markup with added IDs
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function fix($markup, $topLevel = 1, $depth = 2)
|
||||
{
|
||||
$sluggifier = new Sluggifier();
|
||||
|
||||
$tags = $this->determineHeaderTags($topLevel, $depth);
|
||||
$parsed = $this->domParser->str_get_html($markup);
|
||||
|
||||
// Runtime exception for bad code
|
||||
if ( ! $parsed) {
|
||||
throw new RuntimeException("Could not parse HTML");
|
||||
}
|
||||
|
||||
// Extract items
|
||||
foreach ($parsed->find(implode(', ', $tags)) as $tag) {
|
||||
|
||||
// Ignore tags that already have IDs
|
||||
if ($tag->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tag->id = $sluggifier->slugify($tag->title ?: $tag->plaintext);
|
||||
}
|
||||
|
||||
return (string) $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF: TocMarkupFixer.php */
|
85
src/TocTwigExtension.php
Обычный файл
85
src/TocTwigExtension.php
Обычный файл
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace TOC;
|
||||
|
||||
use Twig_Extension;
|
||||
|
||||
/**
|
||||
* Class TocTwigExtension
|
||||
*
|
||||
* @author Casey McLaughlin <caseyamcl@gmail.com>
|
||||
*/
|
||||
class TocTwigExtension extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* @var \TOC\TocGenerator
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
* @var \TOC\TocMarkupFixer
|
||||
*/
|
||||
private $fixer;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \TOC\TocGenerator $generator
|
||||
* @param \TOC\TocMarkupFixer $fixer
|
||||
*/
|
||||
public function __construct(TocGenerator $generator = null, TocMarkupFixer $fixer = null)
|
||||
{
|
||||
$this->generator = $generator ?: new TocGenerator();
|
||||
$this->fixer = $fixer ?: new TocMarkupFixer();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
$filters = parent::getFilters();
|
||||
|
||||
$filters[] = new \Twig_SimpleFilter('add_anchors', function($str, $top = 1, $depth = 2) {
|
||||
return $this->fixer->fix($str, $top, $depth);
|
||||
});
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
$functions = parent::getFunctions();
|
||||
|
||||
// ~~~
|
||||
|
||||
$functions[] = new \Twig_SimpleFunction('toc', function($markup, $top = 1, $depth = 2, $titleTemplate = null) {
|
||||
return ($titleTemplate)
|
||||
? $this->generator->getHtmlItems($markup, $top, $depth, $titleTemplate)
|
||||
: $this->generator->getHtmlItems($markup, $top, $depth);
|
||||
});
|
||||
|
||||
// ~~~
|
||||
|
||||
$functions[] = new \Twig_SimpleFunction('toc_items', function($markup, $top = 1, $depth = 2) {
|
||||
return $this->generator->getItems($markup, $top, $depth);
|
||||
});
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'toc';
|
||||
}
|
||||
}
|
Ссылка в новой задаче
Block a user