Этот коммит содержится в:
Casey McLaughlin
2014-12-30 13:53:32 -05:00
Коммит 7ea43a97fc
22 изменённых файлов: 1751 добавлений и 0 удалений

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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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';
}
}