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

6
.gitignore поставляемый Обычный файл
Просмотреть файл

@ -0,0 +1,6 @@
# files
vendor/
phpunit.xml
# IDE stuff
.idea/workspace.xml

1
.idea/.name сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1 @@
toc

4
.idea/encodings.xml сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

4
.idea/misc.xml сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" />
</project>

8
.idea/modules.xml сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/toc.iml" filepath="$PROJECT_DIR$/.idea/toc.iml" />
</modules>
</component>
</project>

5
.idea/scopes/scope_settings.xml сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

8
.idea/toc.iml сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>

0
CHANGELOG.md Обычный файл
Просмотреть файл

20
LICENSE Обычный файл
Просмотреть файл

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Casey McLaughlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

117
README.md Обычный файл
Просмотреть файл

@ -0,0 +1,117 @@
PHP TOC Generator
=================
Geneates Table of Contents from H1...H6 Tags in HTML Content
------------------------------------------------------------
[![Build Status](https://travis-ci.org/caseyamcl/toc)](https://travis-ci.org/caseyamcl/toc.png)
This library provides a simple, framework-agnostic class to build
a Table-of-Contents from HTML markup. It does so by parsing H1...H6
tags. It can also automatically add appropriate "id" anchor links to content.
Features:
* Generates arrays or HTML &lt;li*gt; tags
* Adds anchor IDs to content
* Specify which *H1*...*H6* heading tags to use at runtime
* Includes Twig Extension for generating TOC lists and compatible markup directly from templates
* PSR-0 thru PSR-2 Compliant
* Composer-compatible
* Unit-tested
In the spirit of KISS philosophy, this library assumes a few things:
1. The hierarchy of your content is defined solely by the header (*H1*...*H6*) tags. All other tags
are ignored.
2. The link titles in the Table of Contents should match either the `title` attribute of the header tag,
or if there is no `title`, the plaintext body of the header tag.
Installation Options
--------------------
Install via [Composer](http://getcomposer.org/) by including the following in your `composer.json` file:
{
"require": {
"caseyamcl/toc": "~1.0",
}
}
Or, drop the `src` folder into your application and use a PSR-0 autoloader to include the files.
Usage
-----
This library does two things with any HTML content passed in:
1. Adds `id` tags to any H1..H6 tags (or header-levels specified at runtime) that do
not already have any. This is to enable anchor links in the content.
2. Generates HTML (or an associative array) of anchor links that can be rendered in your
template.
Basic Example:
```php
$myHtmlContent = <<<END
<h1>This is a header tag with no anchor id</h1>
<p>Lorum ipsum doler sit amet</p>
<h2 id='foo'>This is a header tag with an anchor id</h2>
<p>Stuff here</p>
<h3 id='bar'>This is a header tag with an anchor id</h3>
END;
$tocContent = new \TOC\TocContent($myHtmlContent);
// Generate HTML list of links
echo "<ul>" . $tocContent->getTocListItems('h1', 'h2') . "</ul>";
```
Twig Integration
----------------
This library includes a [Twig](http://twig.sensiolabs.org) extension that enables you to load
TOC lists and compatible markup from your Twig templates.
Specifically, the extension adds three Twig functions:
```twig
{# Generates HTML markup for given htmlContent #}
{# The second two parameters are optional (defaults are h1, h3) #}
<ul>{{ toc(htmlContent, 'h1', 'h3') }}</ul>
{# Generates an array of anchor links for given htmlContent #}
{# The second two parameters are optional (defaults are h1, h3) #}
<ul>
{% for anchor, title in toc_items(htmlContent 'h1', 'h3'): %}
<li class='whatever'><a href='{{ anchor }}'>{{ title }}</a></li>
{% endfor %}
</ul>
{# Adds anchor links (id tags) for given htmlContent #}
{# The second two parameters are optional (defaults are h1, h3) #}
<div class='my_content'>
{{ toc_content(htmlContent, 'h1', 'h3') }}
</div>
```
You may have content in hard-coded in your Twig Template that you want to TOC-ize. An
easy way to do this is to make sure the content is surrounded by `{% block %}...{% endblock %}`
tags, and then just pass in that content to the *toc* functions>
For example:
```twig
{% extends 'base.html.twig' %}
{% block page_content %}
<div class='page_sidebar'>
{{ toc(block('my_writeup'), 'h1', 'h2') }}
</div>
<div class='page_content'>
{{ toc_content(block('my_writeup'), 'h1', 'h2') }}
</div>
{% endblock %}
{% block my_writeup %}
<h1>
{% endblock %}
```

32
composer.json Обычный файл
Просмотреть файл

@ -0,0 +1,32 @@
{
"name": "caseyamcl/toc",
"type": "library",
"description": "Simple Table-of-Contents Generator for PHP. Generates TOCs based off H1...H6 tags",
"keywords": ["table of contents", "toc"],
"homepage": "http://github.com/caseyamcl/toc",
"license": "MIT",
"authors":
[
{
"name": "Casey McLaughlin",
"email": "caseyamcl@gmail.com",
"homepage": "http://caseymclaughlin.com",
"role": "Developer"
}
],
"autoload": {
"psr-4": {
"TOC\\": ["src/", "tests/"]
}
},
"require": {
"php": ">=5.4",
"sunra/php-simple-html-dom-parser": "~1.5",
"cocur/slugify": "~1.0"
},
"require-dev": {
"twig/twig": "~1.13",
"phpunit/phpunit": "~4.0"
}
}

971
composer.lock сгенерированный Обычный файл
Просмотреть файл

@ -0,0 +1,971 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "93020f0823d28448cae16c110561fe2d",
"packages": [
{
"name": "cocur/slugify",
"version": "v1.0",
"source": {
"type": "git",
"url": "https://github.com/cocur/slugify.git",
"reference": "2c929c56997e663747be9bb635e58db918a0134f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cocur/slugify/zipball/2c929c56997e663747be9bb635e58db918a0134f",
"reference": "2c929c56997e663747be9bb635e58db918a0134f",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"codeclimate/php-test-reporter": "dev-master",
"laravel/framework": "~4.1",
"mockery/mockery": "~0.9",
"phpunit/phpunit": "~3.7",
"sami/sami": "~1.3",
"satooshi/php-coveralls": "0.6.*",
"silex/silex": "~1.2",
"symfony/dependency-injection": "~2.4",
"symfony/http-kernel": "~2.4",
"twig/twig": "~1",
"zendframework/zend-modulemanager": "~2.2",
"zendframework/zend-servicemanager": "~2.2",
"zendframework/zend-view": "~2.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Cocur\\Slugify\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ivo Bathke",
"email": "ivo.bathke@gmail.com"
},
{
"name": "Florian Eckerstorfer",
"email": "florian@eckerstorfer.co",
"homepage": "https://florian.ec"
}
],
"description": "Converts a string into a slug.",
"keywords": [
"slug",
"slugify"
],
"time": "2014-11-26 22:45:14"
},
{
"name": "sunra/php-simple-html-dom-parser",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/sunra/php-simple-html-dom-parser.git",
"reference": "a0b80ace086c7e09085669205e1b3c2c9c7a453c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sunra/php-simple-html-dom-parser/zipball/a0b80ace086c7e09085669205e1b3c2c9c7a453c",
"reference": "a0b80ace086c7e09085669205e1b3c2c9c7a453c",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"type": "library",
"autoload": {
"psr-0": {
"Sunra\\PhpSimple\\HtmlDomParser": "Src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sunra",
"email": "sunra@yandex.ru",
"homepage": "http://github.com/sunra"
}
],
"description": "Composer adaptation of: A HTML DOM parser written in PHP5+ let you manipulate HTML in a very easy way! Require PHP 5+. Supports invalid HTML. Find tags on an HTML page with selectors just like jQuery. Extract contents from HTML in a single line.",
"homepage": "https://github.com/sunra/php-simple-html-dom-parser",
"keywords": [
"dom",
"html",
"parser"
],
"time": "2013-05-04 14:32:03"
}
],
"packages-dev": [
{
"name": "doctrine/instantiator",
"version": "1.0.4",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "f976e5de371104877ebc89bd8fecb0019ed9c119"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119",
"reference": "f976e5de371104877ebc89bd8fecb0019ed9c119",
"shasum": ""
},
"require": {
"php": ">=5.3,<8.0-DEV"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
"ext-pdo": "*",
"ext-phar": "*",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "2.0.*@ALPHA"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Instantiator\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.com/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"homepage": "https://github.com/doctrine/instantiator",
"keywords": [
"constructor",
"instantiate"
],
"time": "2014-10-13 12:58:55"
},
{
"name": "phpunit/php-code-coverage",
"version": "2.0.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca158276c1200cc27f5409a5e338486bc0b4fc94",
"reference": "ca158276c1200cc27f5409a5e338486bc0b4fc94",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "~1.3",
"sebastian/environment": "~1.0",
"sebastian/version": "~1.0"
},
"require-dev": {
"ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "~4.1"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.2.1",
"ext-xmlwriter": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"coverage",
"testing",
"xunit"
],
"time": "2014-12-26 13:28:33"
},
{
"name": "phpunit/php-file-iterator",
"version": "1.3.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
"reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"File/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
"homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
"filesystem",
"iterator"
],
"time": "2013-10-10 15:34:57"
},
{
"name": "phpunit/php-text-template",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
"reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"Text/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
],
"time": "2014-01-30 17:20:04"
},
{
"name": "phpunit/php-timer",
"version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
"reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage": "https://github.com/sebastianbergmann/php-timer/",
"keywords": [
"timer"
],
"time": "2013-08-02 07:42:54"
},
{
"name": "phpunit/php-token-stream",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "f8d5d08c56de5cfd592b3340424a81733259a876"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876",
"reference": "f8d5d08c56de5cfd592b3340424a81733259a876",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Wrapper around PHP's tokenizer extension.",
"homepage": "https://github.com/sebastianbergmann/php-token-stream/",
"keywords": [
"tokenizer"
],
"time": "2014-08-31 06:12:13"
},
{
"name": "phpunit/phpunit",
"version": "4.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6a5e49a86ce5e33b8d0657abe145057fc513543a",
"reference": "6a5e49a86ce5e33b8d0657abe145057fc513543a",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpunit/php-code-coverage": "~2.0",
"phpunit/php-file-iterator": "~1.3.2",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "~1.0.2",
"phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.0",
"sebastian/diff": "~1.1",
"sebastian/environment": "~1.1",
"sebastian/exporter": "~1.0",
"sebastian/global-state": "~1.0",
"sebastian/version": "~1.0",
"symfony/yaml": "~2.0"
},
"suggest": {
"phpunit/php-invoker": "~1.1"
},
"bin": [
"phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.4.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "https://phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"time": "2014-12-28 07:57:05"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "c63d2367247365f688544f0d500af90a11a44c65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65",
"reference": "c63d2367247365f688544f0d500af90a11a44c65",
"shasum": ""
},
"require": {
"doctrine/instantiator": "~1.0,>=1.0.1",
"php": ">=5.3.3",
"phpunit/php-text-template": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.3"
},
"suggest": {
"ext-soap": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Mock Object library for PHPUnit",
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
"keywords": [
"mock",
"xunit"
],
"time": "2014-10-03 05:12:11"
},
{
"name": "sebastian/comparator",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "c484a80f97573ab934e37826dba0135a3301b26a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c484a80f97573ab934e37826dba0135a3301b26a",
"reference": "c484a80f97573ab934e37826dba0135a3301b26a",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/diff": "~1.1",
"sebastian/exporter": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides the functionality to compare PHP values for equality",
"homepage": "http://www.github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
"time": "2014-11-16 21:32:38"
},
{
"name": "sebastian/diff",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "5843509fed39dee4b356a306401e9dd1a931fec7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7",
"reference": "5843509fed39dee4b356a306401e9dd1a931fec7",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Kore Nordmann",
"email": "mail@kore-nordmann.de"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Diff implementation",
"homepage": "http://www.github.com/sebastianbergmann/diff",
"keywords": [
"diff"
],
"time": "2014-08-15 10:29:00"
},
{
"name": "sebastian/environment",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e6c71d918088c251b181ba8b3088af4ac336dd7",
"reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides functionality to handle HHVM/PHP environments",
"homepage": "http://www.github.com/sebastianbergmann/environment",
"keywords": [
"Xdebug",
"environment",
"hhvm"
],
"time": "2014-10-25 08:00:45"
},
{
"name": "sebastian/exporter",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0",
"reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
"homepage": "http://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"time": "2014-09-10 00:51:36"
},
{
"name": "sebastian/global-state",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
"reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"suggest": {
"ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Snapshotting of global state",
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"time": "2014-10-06 09:23:50"
},
{
"name": "sebastian/version",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
"reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
"shasum": ""
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2014-03-07 15:35:33"
},
{
"name": "symfony/yaml",
"version": "v2.6.1",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20",
"reference": "3346fc090a3eb6b53d408db2903b241af51dcb20",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.6-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2014-12-02 20:19:20"
},
{
"name": "twig/twig",
"version": "v1.16.3",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "6dc11a1e8ecfc30e2c68aaeb218148409d8e68af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/6dc11a1e8ecfc30e2c68aaeb218148409d8e68af",
"reference": "6dc11a1e8ecfc30e2c68aaeb218148409d8e68af",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.16-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "http://twig.sensiolabs.org/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"time": "2014-12-25 19:58:19"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"platform": {
"php": ">=5.4"
},
"platform-dev": []
}

20
phpunit.xml.dist Обычный файл
Просмотреть файл

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="PHP TOC Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

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

90
tests/TocGeneratorTest.php Обычный файл
Просмотреть файл

@ -0,0 +1,90 @@
<?php
/**
* Created by PhpStorm.
* User: casey
* Date: 12/30/14
* Time: 12:39 PM
*/
use TOC\TocGenerator;
class TocGeneratorTest extends PHPUnit_Framework_TestCase
{
public function testInstantiateSucceeds()
{
$obj = new TocGenerator();
$this->assertInstanceOf('\TOC\TocGenerator', $obj);
}
// ---------------------------------------------------------------
public function testGetItemsMatchesOnlyElementsWithIDs()
{
$obj = new TocGenerator();
$html = "<h1 id='a'>A Header</h1><p>Foobar</p><h2>B Header</h2><h3 id='c'>C Header</h3>";
$this->assertEquals(['a' => 'A Header', 'c' => 'C Header'], $obj->getItems($html, 1, 3));
}
// ---------------------------------------------------------------
public function testGetItemsUsesTitleForDisplayTextWhenAvailableAndPlainTextWhenNot()
{
$obj = new TocGenerator();
$html = '<h1 id="a" title="Foo Bar!">A Header</h1>';
$html .= '<h2 id="b">B Header</h2>';
$html .= '<h3 id="c" title="Baz Biz~">C Header</h3>';
$this->assertEquals(
['a' => 'Foo Bar!', 'b' => 'B Header', 'c' => 'Baz Biz~'],
$obj->getItems($html, 1, 3)
);
}
// ---------------------------------------------------------------
public function testGetItemsGetsOnlyHeaderLevelsSpecified()
{
$obj = new TocGenerator();
$html = '<h1 id="a" title="Foo Bar!">A Header</h1>';
$html .= '<h2 id="b">B Header</h2>';
$html .= '<h3 id="c" title="Baz Biz~">C Header</h3>';
$html .= '<h4 id="d" title="Bal Baf#">D Header</h4>';
$html .= '<h5 id="e" title="Cak Coy%">E Header</h5>';
$html .= '<h6 id="f" title="Dar Dul^">F Header</h6>';
$this->assertCount(1, $obj->getItems($html, 5, 1));
$this->assertCount(2, $obj->getItems($html, 5, 5));
$this->assertCount(6, $obj->getItems($html, -1, 20));
}
// ---------------------------------------------------------------
public function testGetHtmlItemsReturnsExpectedListItems()
{
$obj = new TocGenerator();
$html = '<h1 id="a" title="Foo Bar!">A Header</h1>';
$html .= '<h2 id="b">B Header</h2>';
$html .= '<h3 id="c" title="Baz Biz~">C Header</h3>';
$this->assertEquals(
"<li><a title='Go to Foo Bar!' href='#a'>Foo Bar!</a></li><li><a title='Go to B Header' href='#b'>B Header</a></li><li><a title='Go to Baz Biz~' href='#c'>Baz Biz~</a></li>",
$obj->getHtmlItems($html, 1, 3)
);
}
// ---------------------------------------------------------------
public function testGetItemsReturnsAnEmptyArrayWhenNoContentOrMatches()
{
$obj = new TocGenerator();
$this->assertEquals([], $obj->getItems("<h1>Boo</h1><h2>Bar</h2>"));
$this->assertEquals([], $obj->getItems(""));
}
}
/* EOF: TocGeneratorTest.php */

60
tests/TocMarkupFixerTest.php Обычный файл
Просмотреть файл

@ -0,0 +1,60 @@
<?php
use TOC\TocMarkupFixer;
/**
* Created by PhpStorm.
* User: casey
* Date: 12/30/14
* Time: 1:24 PM
*/
class TocMarkupFixerTest extends PHPUnit_Framework_TestCase
{
public function testInstantiateSucceeds()
{
$obj = new TocMarkupFixer();
$this->assertInstanceOf('\TOC\TocMarkupFixer', $obj);
}
// ---------------------------------------------------------------
public function testFixAddsIdsOnlyToElementsWithoutThem()
{
$obj = new TocMarkupFixer();
$html = "<h1>No ID</h1><h2>Existing ID</h2><h3>Ignored</h3>";
$this->assertEquals(
'<h1 id="no-id">No ID</h1><h2 id="existing-id">Existing ID</h2><h3>Ignored</h3>',
$obj->fix($html, 1, 2)
);
}
// ---------------------------------------------------------------
public function testFixDoesNotDuplicateIdsWhenFixing()
{
$obj = new TocMarkupFixer();
$html = "<h1>FooBar</h1><h2>FooBar</h2><h3>FooBar</h3>";
$this->assertEquals(
'<h1 id="foobar">FooBar</h1><h2 id="foobar-1">FooBar</h2><h3 id="foobar-2">FooBar</h3>',
$obj->fix($html, 1, 3)
);
}
// ---------------------------------------------------------------
public function testFixUsesTitleAttributeWhenAvailable()
{
$obj = new TocMarkupFixer();
$html = "<h1>No ID</h1><h2 title='b'>Existing ID</h2><h3>Ignored</h3>";
$this->assertEquals(
'<h1 id="no-id">No ID</h1><h2 title=\'b\' id="b">Existing ID</h2><h3>Ignored</h3>',
$obj->fix($html, 1, 2)
);
}
}

27
tests/bootstrap.php Обычный файл
Просмотреть файл

@ -0,0 +1,27 @@
<?php
// HEADER HERE
// ---------------------------------------------------------------
/**
* PHP TOC Unit Tests Bootstrap File
*
* @author Casey McLaughlin <caseyamcl@gmail.com>
*/
//Files to ensure exist
$checkFiles['autoload'] = __DIR__.'/../vendor/autoload.php';
$checkFiles[] = __DIR__.'/../vendor/sunra/php-simple-html-dom-parser/README.md';
//Check 'Em
foreach($checkFiles as $file) {
if ( ! file_exists($file)) {
throw new RuntimeException('Install development dependencies to run test suite.');
}
}
//Away we go
$autoload = require_once $checkFiles['autoload'];
/* EOF: bootstrap.php */