(Grav GitSync) Automatic Commit from RealStickman
This commit is contained in:
parent
66b41fd2f5
commit
52bb90a92b
93
plugins/page-toc/CHANGELOG.md
Normal file
93
plugins/page-toc/CHANGELOG.md
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# v3.2.0
|
||||||
|
## 02/23/2022
|
||||||
|
|
||||||
|
1. [](#new)
|
||||||
|
* Support for HTML or Shortcode based headers with custom `id` attributes to specify an anchor
|
||||||
|
* Added German translation
|
||||||
|
|
||||||
|
# v3.1.3
|
||||||
|
## 01/03/2022
|
||||||
|
|
||||||
|
1. [](#new)
|
||||||
|
* Require Grav `v1.7.26` to make use of built in `Plugin::inheritedConfigOption()`
|
||||||
|
* NOTE: `page-toc v3.1.2` was released prior to `Grav v1.7.26` and has been removed
|
||||||
|
2. [](#improved)
|
||||||
|
* Don't force inclusion of `<html>` or `<body>` tags to reduce chance of invalid HTML
|
||||||
|
* Improved `README.md`
|
||||||
|
|
||||||
|
# v3.1.1
|
||||||
|
## 12/16/2021
|
||||||
|
|
||||||
|
1. [](#bugfix)
|
||||||
|
* Fixed some blueprint errors that caused errors on save
|
||||||
|
* Force `start` and `depth` to be integers [#17](https://github.com/trilbymedia/grav-plugin-page-toc/issues/17)
|
||||||
|
|
||||||
|
# v3.1.0
|
||||||
|
## 12/09/2021
|
||||||
|
|
||||||
|
1. [](#new)
|
||||||
|
* **NEW** Added option to automatically copying to clipboard an anchor URL when clicking on it
|
||||||
|
|
||||||
|
# v3.0.0
|
||||||
|
## 12/03/2021
|
||||||
|
|
||||||
|
1. [](#new)
|
||||||
|
* **NEW** Support built-in `anchors` with customization of icon/classes/css etc.
|
||||||
|
* **NEW** `[anchor]` shortcode for creating manual anchors for easy linking to page content
|
||||||
|
* Moved the vendor-based TOC functionality in-plugin to provide more flexibility and additional features
|
||||||
|
* Added several more Twig functions for increased flexibility
|
||||||
|
* Ability to limit the length of a fragment link
|
||||||
|
* Ability to set a custom prefix for anchor links
|
||||||
|
* Added `languages.yaml` file for text translations
|
||||||
|
2. [](#improved)
|
||||||
|
* Independent control over the levels of anchors that should be built and the TOC displayed
|
||||||
|
* `page-toc:` page-level configuration can be set in parent pages and trickles down to child pages
|
||||||
|
* Removed dependency on HTML5 library and use the faster PHP `DOMDocument` class
|
||||||
|
* Translated text for the "Table of Contents" in the `page-toc.html.twig` template
|
||||||
|
|
||||||
|
# v2.0.0
|
||||||
|
## 11/24/2021
|
||||||
|
|
||||||
|
1. [](#new)
|
||||||
|
* Added new `components/page-toc.html.twig` that can be extended and the HTML output modified
|
||||||
|
* Updated core TOC library to latest `3.0.2` version
|
||||||
|
* Requires PHP `7.3.6`
|
||||||
|
* Requires Grav `1.7+`
|
||||||
|
* Added Shortcode-like in-page syntax support. e.g. `[toc]`
|
||||||
|
|
||||||
|
# v1.1.2
|
||||||
|
## 06/01/2021
|
||||||
|
|
||||||
|
1. [](#new)
|
||||||
|
* Added page-toc blueprints under "Advanced" tab for admin
|
||||||
|
1. [](#improved)
|
||||||
|
* Updated to latest `knplabs/knp-menu` library
|
||||||
|
1. [](#bugfix)
|
||||||
|
* Added `|raw` filter to twig output in README.md
|
||||||
|
|
||||||
|
# v1.1.1
|
||||||
|
## 12/02/2020
|
||||||
|
|
||||||
|
1. [](#improved)
|
||||||
|
* Updated to latest `masterminds/html5` and `knplabs/knp-menu` libraries
|
||||||
|
|
||||||
|
# v1.1.0
|
||||||
|
## 04/01/2019
|
||||||
|
|
||||||
|
1. [](#improved)
|
||||||
|
* Updated to latest `caseyamcl/toc` library
|
||||||
|
1. [](#bugfix)
|
||||||
|
* Fixes relative levels [#6](https://github.com/trilbymedia/grav-plugin-page-toc/pull/9)
|
||||||
|
* Fixes incorrect reference to `end` when it should be `depth` [#7](https://github.com/trilbymedia/grav-plugin-page-toc/pull/7)
|
||||||
|
|
||||||
|
# v1.0.1
|
||||||
|
## 03/19/2017
|
||||||
|
|
||||||
|
1. [](#improved)
|
||||||
|
* Fixed issue with `end` not being valid, should be `depth`. Updated README
|
||||||
|
|
||||||
|
# v1.0.0
|
||||||
|
## 08/01/2017
|
||||||
|
|
||||||
|
1. [](#new)
|
||||||
|
* ChangeLog started...
|
21
plugins/page-toc/LICENSE
Normal file
21
plugins/page-toc/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Team Grav
|
||||||
|
|
||||||
|
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.
|
226
plugins/page-toc/README.md
Normal file
226
plugins/page-toc/README.md
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
# Page Toc Plugin (Anchors + Table of Contents)
|
||||||
|
|
||||||
|
The **Page Toc** Plugin is for [Grav CMS](http://github.com/getgrav/grav) that generates anchors based on HTML header tags, and can also create a table of contents from those headers.
|
||||||
|
|
||||||
|
With version `3.0` this plugin is able to automatically generate anchor links with hover-click capability without the need for other plugins (such as the `anchors` plugin). This functionality operates independently from the now optional "table of contents" functionality.
|
||||||
|
|
||||||
|
![](assets/page-toc.png)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Installing the Page Toc plugin can be done in one of two ways. The GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file.
|
||||||
|
|
||||||
|
### GPM Installation (Preferred)
|
||||||
|
|
||||||
|
The simplest way to install this plugin is via the [Grav Package Manager (GPM)](http://learn.getgrav.org/advanced/grav-gpm) through your system's terminal (also called the command line). From the root of your Grav install type:
|
||||||
|
|
||||||
|
bin/gpm install page-toc
|
||||||
|
|
||||||
|
This will install the Page Toc plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/page-toc`.
|
||||||
|
|
||||||
|
### Manual Installation
|
||||||
|
|
||||||
|
To install this plugin, just download the zip version of this repository and unzip it under `/your/site/grav/user/plugins`. Then, rename the folder to `page-toc`. You can find these files on [GitHub](https://github.com/team-grav/grav-plugin-page-toc) or via [GetGrav.org](http://getgrav.org/downloads/plugins#extras).
|
||||||
|
|
||||||
|
You should now have all the plugin files under
|
||||||
|
|
||||||
|
/your/site/grav/user/plugins/page-toc
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Before configuring this plugin, you should copy the `user/plugins/page-toc/page-toc.yaml` to `user/config/plugins/page-toc.yaml` and only edit that copy.
|
||||||
|
|
||||||
|
Here is the default configuration and an explanation of available options:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
enabled: true # Plugin enabled
|
||||||
|
include_css: true # Include CSS
|
||||||
|
active: true # Anchor IDs processed and generated for all pages
|
||||||
|
start: 1 # Start header tag level (1 = h1) for TOC
|
||||||
|
depth: 6 # Depth from start (2 = 2 levels deep) for TOC
|
||||||
|
hclass: # Custom Header TOC styling classes
|
||||||
|
anchors: # Anchor configuration
|
||||||
|
start: 1 # Start header tag level (1 = h1)
|
||||||
|
depth: 6 # Depth from start (2 = 2 levels deep)
|
||||||
|
link: true # Enabled auto-generation of clickable link with fragment
|
||||||
|
aria: Anchor # Aria label to use
|
||||||
|
class: # Custom Header anchor styling classes
|
||||||
|
icon: '#' # Icon to use, can be a symbol, emoji, ascii etc.
|
||||||
|
position: after # Position to put the anchor, `before|after`
|
||||||
|
copy_to_clipboard: true # Copy to clipboard functionality (coming soon)
|
||||||
|
slug_maxlen: 25 # Max length of slugs used for anchors
|
||||||
|
slug_prefix: # A prefix used in front of generated slugs
|
||||||
|
```
|
||||||
|
|
||||||
|
> You can now have `page-toc` automatically add anchors without there being a table of contents being used, just ensure `active` to `true`.
|
||||||
|
|
||||||
|
By default, The plugin is `active` and will add header id attributes anchors for each header level found in a page. You can set `active: false` and then activate on a page basis by adding this to the page frontmatter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
page-toc:
|
||||||
|
active: true
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also configure which header tags to start and depth on when building the id attribute anchors by changing the `start` and `depth` values. This can also be done on a per-page basis.
|
||||||
|
|
||||||
|
For example if you had a start of `3` and a depth of `3` you would get a TOC for `h3`, `h4`, and `h5`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Shortcode-like syntax in your content
|
||||||
|
|
||||||
|
You can use the following shortcode-like syntax in your content:
|
||||||
|
|
||||||
|
```md
|
||||||
|
[TOC] or [TOC/] or [toc] or [toc /]
|
||||||
|
```
|
||||||
|
|
||||||
|
This will replace the shortcode syntax with the Table of Contents with the `components/page-toc.html.twig` Twig template. Either the default one included in the `page-toc` plugin or an overridden version from your theme.
|
||||||
|
|
||||||
|
For example in Quark theme, you will need to create a folder called `components/` under `templates/` so the file will be copied to:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
user/themes/quark/templates/components/page-toc.html.twig
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: It's not required to set the TOC plugin `active` if you use the shortcode syntax in your content. That is a good enough indication that you want the plugin to be active.
|
||||||
|
|
||||||
|
### Anchor Shortcode
|
||||||
|
|
||||||
|
Page TOC now includes a `anchor` shortcode that allows you to manually add linkable fragments in your content.
|
||||||
|
The shortcode will automatically generate the link if no options are provided. Alternatively you can use the bbode syntax of `anchor="some-custom-id"` or you can explicity set it. You can also set a `prefix` and let the shortcode autogenerate the rest.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
|
||||||
|
Ut sed nisl suscipit metus sollicitudin [anchor]<span>ornare</span>[/anchor] nec vitae nulla. In pretium massa ex, in [anchor="vulputate"]vulputate tellus[/anchor] accumsan vel.
|
||||||
|
|
||||||
|
Nullam [anchor id="tempor"]tempor quis lorem[/anchor] venenatis finibus. Curabitur dapibus nulla sed tristique pretium. Nullam tempor quis [anchor prefix="sec2.2-"]lorem venenatis finibus[/anchor].
|
||||||
|
```
|
||||||
|
|
||||||
|
An example of the resulting HTML link looks like:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<a id="tempor" href="#tempor" class="inline-anchor" aria-label="Anchor">tempor quis lorem</a>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `inline-anchor` CSS class is used by shortcodes and any manually generated elements so it can be styled as independently from other links or anchored headers.
|
||||||
|
|
||||||
|
### Twig Templating
|
||||||
|
|
||||||
|
When the plugin is `active` it will add anchors to the header tags of the page content as configured. You can simply include the provided Twig template:
|
||||||
|
|
||||||
|
```twig
|
||||||
|
{% block content %}
|
||||||
|
{% include 'components/page-toc.html.twig' %}
|
||||||
|
{{ content|raw }}
|
||||||
|
{% endblock %}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also add your **Table of Contents** HTML in your Twig template directly with the provided `toc()` Twig function:
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```twig
|
||||||
|
{% if active or toc_config_var('active') %}
|
||||||
|
<div class="page-toc">
|
||||||
|
{% set table_of_contents = toc(page.content) %}
|
||||||
|
{% if table_of_contents is not empty %}
|
||||||
|
<h4>{{ 'PLUGIN_PAGE_TOC.TABLE_OF_CONTENTS'|t }}</h4>
|
||||||
|
{{ table_of_contents|raw }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `toc_ordered()` Twig function does the same things as a the `toc()` function, except it uses an ordered list instead of an unordered one.
|
||||||
|
|
||||||
|
or via the `toc_items()` function which rather than returning HTML directly returns objects and you can manipulate the output as needed:
|
||||||
|
|
||||||
|
```twig
|
||||||
|
{% macro toc_loop(items) %}
|
||||||
|
{% import _self as self %}
|
||||||
|
{% for item in items %}
|
||||||
|
{% set class = loop.first ? 'first' : loop.last ? 'last' : null %}
|
||||||
|
<li {% if class %}class="{{ class }}"{% endif %}>
|
||||||
|
<a href="{{ item.uri }}">{{ item.label }}</a>
|
||||||
|
{% if item.children|length > 0 %}
|
||||||
|
<ul>
|
||||||
|
{{ _self.toc_loop(item.children) }}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% if config.get('plugins.page-toc.active') or attribute(page.header, 'page-toc').active %}
|
||||||
|
<div class="page-toc">
|
||||||
|
{% set table_of_contents = toc_items(page.content) %}
|
||||||
|
{% if table_of_contents is not empty %}
|
||||||
|
<h4>Table of Contents</h4>
|
||||||
|
<ul class="page-toc">
|
||||||
|
{{ _self.toc_loop(table_of_contents.children) }}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
To explictly build a table of contents for a block of content:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
{% block my_content %}
|
||||||
|
# Header 1
|
||||||
|
|
||||||
|
## Header 1.1
|
||||||
|
|
||||||
|
Nullam tempor quis lorem venenatis finibus. Maecenas ut condimentum nibh. Ut sed nisl suscipit metus sollicitudin ornare nec vitae nulla. Integer sed tortor eu ligula interdum rhoncus. Sed pulvinar ut massa et ullamcorper. Curabitur bibendum ante orci, nec porttitor dolor suscipit quis. Nulla et eros enim.
|
||||||
|
|
||||||
|
### Header 1.1.1
|
||||||
|
|
||||||
|
Integer sed tortor eu ligula interdum rhoncus.
|
||||||
|
|
||||||
|
## Header 1.2
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
#### Table O' Contents
|
||||||
|
{{ toc(block('my_content'), 2, 1) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `add_anchors()` twig funtion can take a string or a block of content and automatically adds anchors to any headers found per the configuration for the page, but you can override the start and depth. For example here we have a Twig block but we just want to add anchors to the H2 tags:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
{% block my_content %}
|
||||||
|
# Header 1
|
||||||
|
|
||||||
|
## Header 1.1
|
||||||
|
|
||||||
|
Nullam tempor quis lorem venenatis finibus. Maecenas ut condimentum nibh. Ut sed nisl suscipit metus sollicitudin ornare nec vitae nulla. Integer sed tortor eu ligula interdum rhoncus. Sed pulvinar ut massa et ullamcorper. Curabitur bibendum ante orci, nec porttitor dolor suscipit quis. Nulla et eros enim.
|
||||||
|
|
||||||
|
### Header 1.1.1
|
||||||
|
|
||||||
|
Integer sed tortor eu ligula interdum rhoncus.
|
||||||
|
|
||||||
|
## Header 1.2
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
#### Anchors Away!
|
||||||
|
{{ add_anchors(block('my_content'), 2, 1) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Limiting levels in output
|
||||||
|
|
||||||
|
As well as limiting the levels that the page TOC plugin will use in the table of contents, you can also limit the levels that are actually displayed. To do this you can pass an optional `start`, and `depth` value to the `toc()`, `toc_ordered()` , `toc_items()` and `add_anchors()` Twig functions:
|
||||||
|
|
||||||
|
```twig
|
||||||
|
{% set table_of_contents = toc(page.content, 3, 3) %}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will only display `H3` , and **3** levels deeper (up to `H5`) in the TOC output.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
The majority of this plugin's functionality is provided by the [PHP TOC Generator](https://github.com/caseyamcl/toc) library by [Casey McLaughlin](https://github.com/caseyamcl). So Thanks for making this plugin for Grav possible!
|
||||||
|
|
||||||
|
|
22
plugins/page-toc/assets/page-toc-anchors.css
Normal file
22
plugins/page-toc/assets/page-toc-anchors.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.toc-anchor {
|
||||||
|
transition: hover 0.5s ease;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-anchor.after {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-anchor.before {
|
||||||
|
margin-left: -22px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-anchor:after {
|
||||||
|
content: attr(data-anchor-icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover > .toc-anchor, .toc-anchor:focus {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
10
plugins/page-toc/assets/page-toc-anchors.js
Normal file
10
plugins/page-toc/assets/page-toc-anchors.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
document.body.addEventListener('click', (event) => {
|
||||||
|
if (typeof event.target.dataset.anchorIcon !== 'undefined') {
|
||||||
|
const href = event.target.href;
|
||||||
|
navigator.clipboard.writeText(href)
|
||||||
|
.then(() => {})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Unable to copy to clipboard the anchor', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, true);
|
BIN
plugins/page-toc/assets/page-toc.png
Normal file
BIN
plugins/page-toc/assets/page-toc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 331 KiB |
170
plugins/page-toc/blueprints.yaml
Normal file
170
plugins/page-toc/blueprints.yaml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
name: Page Toc
|
||||||
|
type: plugin
|
||||||
|
slug: page-toc
|
||||||
|
version: 3.2.0
|
||||||
|
description: Generate a table of contents and anchors from a page
|
||||||
|
icon: list
|
||||||
|
author:
|
||||||
|
name: Trilby Media, LLC
|
||||||
|
email: hello@trilby.media
|
||||||
|
url: http://trilby.media
|
||||||
|
homepage: http://trilby.media
|
||||||
|
keywords: grav, plugin, toc, anchors
|
||||||
|
bugs: https://github.com/trilbymedia/grav-plugin-page-toc/issues
|
||||||
|
docs: https://github.com/trilbymedia/grav-plugin-page-toc/blob/develop/README.md
|
||||||
|
license: MIT
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- { name: grav, version: '>=1.7.26' }
|
||||||
|
|
||||||
|
form:
|
||||||
|
validation: strict
|
||||||
|
fields:
|
||||||
|
enabled:
|
||||||
|
type: toggle
|
||||||
|
label: PLUGIN_ADMIN.PLUGIN_STATUS
|
||||||
|
highlight: 1
|
||||||
|
default: 1
|
||||||
|
options:
|
||||||
|
1: PLUGIN_ADMIN.ENABLED
|
||||||
|
0: PLUGIN_ADMIN.DISABLED
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
include_css:
|
||||||
|
type: toggle
|
||||||
|
label: PLUGIN_PAGE_TOC.INCLUDE_CSS
|
||||||
|
highlight: 1
|
||||||
|
default: 1
|
||||||
|
options:
|
||||||
|
1: PLUGIN_ADMIN.ENABLED
|
||||||
|
0: PLUGIN_ADMIN.DISABLED
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
active:
|
||||||
|
type: toggle
|
||||||
|
label: PLUGIN_PAGE_TOC.ACTIVE_BY_DEFAULT
|
||||||
|
highlight: 1
|
||||||
|
default: 1
|
||||||
|
options:
|
||||||
|
1: PLUGIN_ADMIN.ENABLED
|
||||||
|
0: PLUGIN_ADMIN.DISABLED
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
toc_section:
|
||||||
|
type: section
|
||||||
|
title: PLUGIN_PAGE_TOC.TOC_SECTION
|
||||||
|
underline: true
|
||||||
|
|
||||||
|
fields:
|
||||||
|
start:
|
||||||
|
type: select
|
||||||
|
label: PLUGIN_PAGE_TOC.START_TOC_HEADERS
|
||||||
|
help: PLUGIN_PAGE_TOC.START_TOC_HEADERS_HELP
|
||||||
|
size: x-small
|
||||||
|
classes: fancy
|
||||||
|
options:
|
||||||
|
1: H1
|
||||||
|
2: H2
|
||||||
|
3: H3
|
||||||
|
4: H4
|
||||||
|
5: H5
|
||||||
|
6: H6
|
||||||
|
validate:
|
||||||
|
type: number
|
||||||
|
depth:
|
||||||
|
type: range
|
||||||
|
label: PLUGIN_PAGE_TOC.DEPTH_TOC_HEADERS
|
||||||
|
help: PLUGIN_PAGE_TOC.DEPTH_TOC_HEADERS_HELP
|
||||||
|
classes: fancy
|
||||||
|
validate:
|
||||||
|
min: 1
|
||||||
|
max: 6
|
||||||
|
hclass:
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.HEADER_CSS_CLASSES
|
||||||
|
help: PLUGIN_PAGE_TOC.HEADER_CSS_CLASSES_HELP
|
||||||
|
|
||||||
|
anchors_section:
|
||||||
|
type: section
|
||||||
|
title: PLUGIN_PAGE_TOC.ANCHORS_SECTION
|
||||||
|
underline: true
|
||||||
|
|
||||||
|
fields:
|
||||||
|
anchors.start:
|
||||||
|
type: select
|
||||||
|
label: PLUGIN_PAGE_TOC.START_ANCHOR_HEADERS
|
||||||
|
size: x-small
|
||||||
|
classes: fancy
|
||||||
|
options:
|
||||||
|
1: H1
|
||||||
|
2: H2
|
||||||
|
3: H3
|
||||||
|
4: H4
|
||||||
|
5: H5
|
||||||
|
6: H6
|
||||||
|
validate:
|
||||||
|
type: number
|
||||||
|
anchors.depth:
|
||||||
|
type: range
|
||||||
|
label: PLUGIN_PAGE_TOC.DEPTH_ANCHOR_HEADERS
|
||||||
|
help: PLUGIN_PAGE_TOC.DEPTH_ANCHOR_HEADERS_HELP
|
||||||
|
classes: fancy
|
||||||
|
validate:
|
||||||
|
min: 1
|
||||||
|
max: 6
|
||||||
|
anchors.link:
|
||||||
|
type: toggle
|
||||||
|
label: PLUGIN_PAGE_TOC.LINK_ANCHOR_HEADERS
|
||||||
|
highlight: 1
|
||||||
|
default: 1
|
||||||
|
options:
|
||||||
|
1: Enabled
|
||||||
|
0: Disabled
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
anchors.aria:
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.ARIA_LABEL
|
||||||
|
default: Anchor
|
||||||
|
anchors.class:
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.ANCHORS_CLASS
|
||||||
|
help: PLUGIN_PAGE_TOC.ANCHORS_CLASS_HELP
|
||||||
|
anchors.icon:
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.ANCHORS_ICON
|
||||||
|
help: PLUGIN_PAGE_TOC.ANCHORS_ICON_HELP
|
||||||
|
default: '#'
|
||||||
|
size: x-small
|
||||||
|
anchors.position:
|
||||||
|
type: select
|
||||||
|
label: PLUGIN_PAGE_TOC.ANCHORS_POSITION
|
||||||
|
help: PLUGIN_PAGE_TOC.ANCHORS_POSITION_HELP
|
||||||
|
size: small
|
||||||
|
default: after
|
||||||
|
options:
|
||||||
|
before: PLUGIN_PAGE_TOC.BEFORE_TEXT
|
||||||
|
after: PLUGIN_PAGE_TOC.AFTER_TEXT
|
||||||
|
anchors.copy_to_clipboard:
|
||||||
|
type: toggle
|
||||||
|
label: PLUGIN_PAGE_TOC.COPY_TO_CLIPBOARD
|
||||||
|
help: PLUGIN_PAGE_TOC.COPY_TO_CLIPBOARD_HELP
|
||||||
|
highlight: 1
|
||||||
|
default: 1
|
||||||
|
options:
|
||||||
|
1: Enabled
|
||||||
|
0: Disabled
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
anchors.slug_maxlen:
|
||||||
|
type: number
|
||||||
|
label: PLUGIN_PAGE_TOC.SLUG_MAXLEN
|
||||||
|
help: PLUGIN_PAGE_TOC.SLUG_MAXLEN_HELP
|
||||||
|
size: x-small
|
||||||
|
default: 25
|
||||||
|
append: 'chars'
|
||||||
|
anchors.slug_prefix:
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.SLUG_PREFIX
|
||||||
|
help: PLUGIN_PAGE_TOC.SLUG_PREFIX_HELP
|
142
plugins/page-toc/blueprints/page-toc.yaml
Normal file
142
plugins/page-toc/blueprints/page-toc.yaml
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
form:
|
||||||
|
fields:
|
||||||
|
tabs:
|
||||||
|
fields:
|
||||||
|
advanced:
|
||||||
|
type: tab
|
||||||
|
|
||||||
|
fields:
|
||||||
|
header.page-toc-section:
|
||||||
|
type: section
|
||||||
|
title: Page Table-of-Contents
|
||||||
|
underline: true
|
||||||
|
|
||||||
|
fields:
|
||||||
|
header.page-toc.active:
|
||||||
|
toggleable: true
|
||||||
|
type: toggle
|
||||||
|
label: PLUGIN_PAGE_TOC.ACTIVE_BY_DEFAULT_PAGE
|
||||||
|
help: PLUGIN_PAGE_TOC.ACTIVE_BY_DEFAULT_PAGE_HELP
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'active']
|
||||||
|
options:
|
||||||
|
1: PLUGIN_ADMIN.YES
|
||||||
|
0: PLUGIN_ADMIN.NO
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
header.page-toc.start:
|
||||||
|
toggleable: true
|
||||||
|
type: select
|
||||||
|
size: x-small
|
||||||
|
label: PLUGIN_PAGE_TOC.START_TOC_HEADERS
|
||||||
|
help: PLUGIN_PAGE_TOC.START_TOC_HEADERS_HELP
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'start']
|
||||||
|
options:
|
||||||
|
1: H1
|
||||||
|
2: H2
|
||||||
|
3: H3
|
||||||
|
4: H4
|
||||||
|
5: H5
|
||||||
|
6: H6
|
||||||
|
validate:
|
||||||
|
type: number
|
||||||
|
header.page-toc.depth:
|
||||||
|
toggleable: true
|
||||||
|
type: range
|
||||||
|
label: PLUGIN_PAGE_TOC.DEPTH_TOC_HEADERS
|
||||||
|
help: PLUGIN_PAGE_TOC.DEPTH_TOC_HEADERS_HELP
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'depth']
|
||||||
|
validate:
|
||||||
|
min: 1
|
||||||
|
max: 6
|
||||||
|
header.page-toc.hclass:
|
||||||
|
toggleable: true
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.HEADER_CSS_CLASSES
|
||||||
|
help: PLUGIN_PAGE_TOC.HEADER_CSS_CLASSES_HELP
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'hclass']
|
||||||
|
|
||||||
|
header.page-toc-anchors-section:
|
||||||
|
type: section
|
||||||
|
title: PLUGIN_PAGE_TOC.PAGE_ANCHORS_SECTION
|
||||||
|
underline: true
|
||||||
|
|
||||||
|
fields:
|
||||||
|
header.page-toc.anchors.start:
|
||||||
|
toggleable: true
|
||||||
|
type: select
|
||||||
|
label: PLUGIN_PAGE_TOC.START_ANCHOR_HEADERS
|
||||||
|
size: x-small
|
||||||
|
classes: fancy
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.start']
|
||||||
|
options:
|
||||||
|
1: H1
|
||||||
|
2: H2
|
||||||
|
3: H3
|
||||||
|
4: H4
|
||||||
|
5: H5
|
||||||
|
6: H6
|
||||||
|
validate:
|
||||||
|
type: number
|
||||||
|
header.page-toc.anchors.depth:
|
||||||
|
toggleable: true
|
||||||
|
type: range
|
||||||
|
label: PLUGIN_PAGE_TOC.DEPTH_ANCHOR_HEADERS
|
||||||
|
classes: fancy
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.depth']
|
||||||
|
validate:
|
||||||
|
min: 1
|
||||||
|
max: 6
|
||||||
|
header.page-toc.anchors.link:
|
||||||
|
toggleable: true
|
||||||
|
type: toggle
|
||||||
|
label: PLUGIN_PAGE_TOC.LINK_ANCHOR_HEADERS
|
||||||
|
highlight: 1
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.link']
|
||||||
|
options:
|
||||||
|
1: Enabled
|
||||||
|
0: Disabled
|
||||||
|
validate:
|
||||||
|
type: bool
|
||||||
|
header.page-toc.anchors.aria:
|
||||||
|
toggleable: true
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.ARIA_LABEL
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.aria']
|
||||||
|
header.page-toc.anchors.class:
|
||||||
|
toggleable: true
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.ANCHORS_CLASS
|
||||||
|
help: PLUGIN_PAGE_TOC.ANCHORS_CLASS_HELP
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.class']
|
||||||
|
header.page-toc.anchors.icon:
|
||||||
|
toggleable: true
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.ANCHORS_ICON
|
||||||
|
help: PLUGIN_PAGE_TOC.ANCHORS_ICON_HELP
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.icon']
|
||||||
|
size: x-small
|
||||||
|
header.page-toc.anchors.position:
|
||||||
|
toggleable: true
|
||||||
|
type: select
|
||||||
|
label: PLUGIN_PAGE_TOC.ANCHORS_POSITION
|
||||||
|
help: PLUGIN_PAGE_TOC.ANCHORS_POSITION_HELP
|
||||||
|
size: small
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.position']
|
||||||
|
options:
|
||||||
|
before: PLUGIN_PAGE_TOC.BEFORE_TEXT
|
||||||
|
after: PLUGIN_PAGE_TOC.AFTER_TEXT
|
||||||
|
header.page-toc.anchors.slug_maxlen:
|
||||||
|
toggleable: true
|
||||||
|
type: number
|
||||||
|
label: PLUGIN_PAGE_TOC.SLUG_MAXLEN
|
||||||
|
help: PLUGIN_PAGE_TOC.SLUG_MAXLEN_HELP
|
||||||
|
size: x-small
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.slug_maxlen']
|
||||||
|
append: 'chars'
|
||||||
|
header.page-toc.anchors.slug_prefix:
|
||||||
|
toggleable: true
|
||||||
|
type: text
|
||||||
|
label: PLUGIN_PAGE_TOC.SLUG_PREFIX
|
||||||
|
help: PLUGIN_PAGE_TOC.SLUG_PREFIX_HELP
|
||||||
|
data-default@: ['\Grav\Plugin\PageTOCPlugin::configVar', 'anchors.slug_prefix']
|
89
plugins/page-toc/classes/HtmlHelper.php
Normal file
89
plugins/page-toc/classes/HtmlHelper.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PageTOC
|
||||||
|
*
|
||||||
|
* This plugin allows creation of Table of Contents + Link Anchors
|
||||||
|
*
|
||||||
|
* Based on the original version https://github.com/caseyamcl/toc
|
||||||
|
* by Casey McLaughlin <caseyamcl@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under MIT, see LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Grav\Plugin\PageToc;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
use DOMDocument;
|
||||||
|
use DomElement;
|
||||||
|
use DOMXPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait that helps with HTML-related operations
|
||||||
|
*
|
||||||
|
* @package TOC
|
||||||
|
*/
|
||||||
|
trait HtmlHelper
|
||||||
|
{
|
||||||
|
protected function getHTMLParser($markup)
|
||||||
|
{
|
||||||
|
libxml_use_internal_errors(true);
|
||||||
|
$domDocument = new \DOMDocument();
|
||||||
|
$domDocument->loadHTML(mb_convert_encoding("<page-toc>$markup</page-toc>", 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
$domDocument->preserveWhiteSpace = true;
|
||||||
|
return $domDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a topLevel and depth to H1..H6 tags array
|
||||||
|
*
|
||||||
|
* @param int $topLevel
|
||||||
|
* @param int $depth
|
||||||
|
* @return array|string[] Array of header tags; ex: ['h1', 'h2', 'h3']
|
||||||
|
*/
|
||||||
|
protected function determineHeaderTags(int $topLevel, int $depth): array
|
||||||
|
{
|
||||||
|
$desired = range((int) $topLevel, (int) $topLevel + ((int) $depth - 1));
|
||||||
|
$allowed = [1, 2, 3, 4, 5, 6];
|
||||||
|
|
||||||
|
return array_map(function ($val) {
|
||||||
|
return 'h' . $val;
|
||||||
|
}, array_intersect($desired, $allowed));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverse Header Tags in DOM Document
|
||||||
|
*
|
||||||
|
* @param DOMDocument $domDocument
|
||||||
|
* @param int $topLevel
|
||||||
|
* @param int $depth
|
||||||
|
* @return ArrayIterator<int,DomElement>
|
||||||
|
*/
|
||||||
|
protected function traverseHeaderTags(DOMDocument $domDocument, int $topLevel, int $depth): ArrayIterator
|
||||||
|
{
|
||||||
|
$xQueryResults = new DOMXPath($domDocument);
|
||||||
|
|
||||||
|
$xpathQuery = sprintf(
|
||||||
|
"//*[%s]",
|
||||||
|
implode(' or ', array_map(function ($v) {
|
||||||
|
return sprintf('local-name() = "%s"', $v);
|
||||||
|
}, $this->determineHeaderTags($topLevel, $depth)))
|
||||||
|
);
|
||||||
|
|
||||||
|
$nodes = [];
|
||||||
|
$xQueryResults = $xQueryResults->query($xpathQuery);
|
||||||
|
|
||||||
|
if ($xQueryResults) {
|
||||||
|
foreach ($xQueryResults as $node) {
|
||||||
|
$nodes[] = $node;
|
||||||
|
}
|
||||||
|
return new ArrayIterator($nodes);
|
||||||
|
} else {
|
||||||
|
return new ArrayIterator([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
plugins/page-toc/classes/MarkupFixer.php
Normal file
82
plugins/page-toc/classes/MarkupFixer.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PageTOC
|
||||||
|
*
|
||||||
|
* This plugin allows creation of Table of Contents + Link Anchors
|
||||||
|
*
|
||||||
|
* Based on the original version https://github.com/caseyamcl/toc
|
||||||
|
* by Casey McLaughlin <caseyamcl@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under MIT, see LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Grav\Plugin\PageToc;
|
||||||
|
|
||||||
|
use DOMElement;
|
||||||
|
use RuntimeException;
|
||||||
|
use Cocur\Slugify\SlugifyInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TOC Markup Fixer adds `id` attributes to all H1...H6 tags where they do not
|
||||||
|
* already exist
|
||||||
|
*
|
||||||
|
* @author Casey McLaughlin <caseyamcl@gmail.com>
|
||||||
|
*/
|
||||||
|
class MarkupFixer
|
||||||
|
{
|
||||||
|
use HtmlHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix markup
|
||||||
|
*
|
||||||
|
* @param string $markup
|
||||||
|
* @param int $start
|
||||||
|
* @param int $depth
|
||||||
|
* @param array $options
|
||||||
|
* @return string Markup with added IDs
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function fix(string $markup, array $options = []): ?string
|
||||||
|
{
|
||||||
|
$start = (int) $options['start'] ?? 1;
|
||||||
|
$depth = (int) $options['depth'] ?? 6;
|
||||||
|
$domDocument = $this->getHTMLParser($markup);
|
||||||
|
$slugger = new UniqueSlugify();
|
||||||
|
|
||||||
|
/** @var DOMElement $node */
|
||||||
|
foreach ($this->traverseHeaderTags($domDocument, $start, $depth) as $node) {
|
||||||
|
if ($node->getAttribute('id')) {
|
||||||
|
$slug = $node->getAttribute('id');
|
||||||
|
} else {
|
||||||
|
$slug = $slugger->slugify($node->getAttribute('title') ?: $node->textContent, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->setAttribute('id', $slug);
|
||||||
|
|
||||||
|
if ($options['hclass']) {
|
||||||
|
$class = $node->getAttribute('class');
|
||||||
|
$node->setAttribute('class', trim("$class {$options['hclass']}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($options['link']) {
|
||||||
|
$link = $domDocument->createElement("a");
|
||||||
|
$class = isset($options['class']) ? " {$options['class']}" : "";
|
||||||
|
$link->setAttribute('href', "#$slug");
|
||||||
|
$link->setAttribute('class', "toc-anchor {$options['position']}$class");
|
||||||
|
$link->setAttribute('data-anchor-icon', $options['icon']);
|
||||||
|
$link->setAttribute('aria-label', $options['aria']);
|
||||||
|
if ($options['position'] == 'after') {
|
||||||
|
$node->appendChild($link);
|
||||||
|
} else {
|
||||||
|
$node->insertBefore($link, $node->firstChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$markup = $domDocument->saveHTML($domDocument->getElementsByTagName('page-toc')->item(0));
|
||||||
|
return str_replace(['<page-toc>', '</page-toc>'], '', $markup);
|
||||||
|
}
|
||||||
|
}
|
68
plugins/page-toc/classes/OrderedListRenderer.php
Normal file
68
plugins/page-toc/classes/OrderedListRenderer.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PageTOC
|
||||||
|
*
|
||||||
|
* This plugin allows creation of Table of Contents + Link Anchors
|
||||||
|
*
|
||||||
|
* Based on the original version https://github.com/caseyamcl/toc
|
||||||
|
* by Casey McLaughlin <caseyamcl@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under MIT, see LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Grav\Plugin\PageToc;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\Renderer\ListRenderer;
|
||||||
|
|
||||||
|
use function str_repeat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class OrderedListRenderer
|
||||||
|
*
|
||||||
|
* @package TOC
|
||||||
|
*/
|
||||||
|
class OrderedListRenderer extends ListRenderer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param ItemInterface $item
|
||||||
|
* @param array<string> $attributes
|
||||||
|
* @param array<mixed> $options
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function renderList(ItemInterface $item, array $attributes, array $options): string
|
||||||
|
{
|
||||||
|
if (!$item->hasChildren() || 0 === $options['depth'] || !$item->getDisplayChildren()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = $this->format(
|
||||||
|
'<ol' . $this->renderHtmlAttributes($attributes) . '>',
|
||||||
|
'ol',
|
||||||
|
$item->getLevel(),
|
||||||
|
$options
|
||||||
|
);
|
||||||
|
|
||||||
|
$html .= $this->renderChildren($item, $options);
|
||||||
|
$html .= $this->format('</ol>', 'ol', $item->getLevel(), $options);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $html
|
||||||
|
* @param string $type
|
||||||
|
* @param int $level
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function format(string $html, string $type, int $level, array $options): string
|
||||||
|
{
|
||||||
|
return $type === 'ol'
|
||||||
|
? str_repeat(' ', $level * 4) . $html . "\n"
|
||||||
|
: parent::format($html, $type, $level, $options);
|
||||||
|
}
|
||||||
|
}
|
179
plugins/page-toc/classes/TocGenerator.php
Normal file
179
plugins/page-toc/classes/TocGenerator.php
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PageTOC
|
||||||
|
*
|
||||||
|
* This plugin allows creation of Table of Contents + Link Anchors
|
||||||
|
*
|
||||||
|
* Based on the original version https://github.com/caseyamcl/toc
|
||||||
|
* by Casey McLaughlin <caseyamcl@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under MIT, see LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Grav\Plugin\PageToc;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\Matcher\Matcher;
|
||||||
|
use Knp\Menu\MenuFactory;
|
||||||
|
use Knp\Menu\MenuItem;
|
||||||
|
use Knp\Menu\Renderer\ListRenderer;
|
||||||
|
use Knp\Menu\Renderer\RendererInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table Of Contents Generator generates TOCs from HTML Markup
|
||||||
|
*
|
||||||
|
* @author Casey McLaughlin <caseyamcl@gmail.com>
|
||||||
|
*/
|
||||||
|
class TocGenerator
|
||||||
|
{
|
||||||
|
use HtmlHelper;
|
||||||
|
|
||||||
|
private const DEFAULT_NAME = 'TOC';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuFactory
|
||||||
|
*/
|
||||||
|
private $menuFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->menuFactory = new MenuFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Menu
|
||||||
|
*
|
||||||
|
* Returns a KNP Menu object, which can be traversed or rendered
|
||||||
|
*
|
||||||
|
* @param string $markup Content to get items fro $this->getItems($markup, $topLevel, $depth)m
|
||||||
|
* @param int $topLevel Top Header (1 through 6)
|
||||||
|
* @param int $depth Depth (1 through 6)
|
||||||
|
* @return ItemInterface KNP Menu
|
||||||
|
*/
|
||||||
|
public function getMenu(string $markup, int $topLevel = 1, int $depth = 6): ItemInterface
|
||||||
|
{
|
||||||
|
$menu = $this->menuFactory->createItem(static::DEFAULT_NAME);
|
||||||
|
|
||||||
|
if (trim($markup) == '') {
|
||||||
|
return $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagsToMatch = $this->determineHeaderTags($topLevel, $depth);
|
||||||
|
$lastElem = $menu;
|
||||||
|
|
||||||
|
$domDocument = $this->getHTMLParser($markup);
|
||||||
|
|
||||||
|
foreach ($this->traverseHeaderTags($domDocument, $topLevel, $depth) as $i => $node) {
|
||||||
|
if (! $node->hasAttribute('id')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagName = $node->tagName;
|
||||||
|
$level = array_search(strtolower($tagName), $tagsToMatch) + 1;
|
||||||
|
|
||||||
|
/** @var MenuItem $parent */
|
||||||
|
if ($level == 1) {
|
||||||
|
$parent = $menu;
|
||||||
|
} elseif ($level == $lastElem->getLevel()) {
|
||||||
|
$parent = $lastElem->getParent();
|
||||||
|
} elseif ($level > $lastElem->getLevel()) {
|
||||||
|
$parent = $lastElem;
|
||||||
|
for ($i = $lastElem->getLevel(); $i < ($level - 1); $i++) {
|
||||||
|
$parent = $parent->addChild('');
|
||||||
|
}
|
||||||
|
} else { //if ($level < $lastElem->getLevel())
|
||||||
|
$parent = $lastElem->getParent();
|
||||||
|
while ($parent->getLevel() > $level - 1) {
|
||||||
|
$parent = $parent->getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastElem = $parent->addChild(
|
||||||
|
$node->getAttribute('id'),
|
||||||
|
[
|
||||||
|
'label' => $node->getAttribute('title') ?: $node->textContent,
|
||||||
|
'uri' => '#' . $node->getAttribute('id')
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->trimMenu($menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trim empty items from the menu
|
||||||
|
*
|
||||||
|
* @param ItemInterface $menuItem
|
||||||
|
* @return ItemInterface
|
||||||
|
*/
|
||||||
|
protected function trimMenu(ItemInterface $menuItem): ItemInterface
|
||||||
|
{
|
||||||
|
// if any of these circumstances are true, then just bail and return the menu item
|
||||||
|
if (
|
||||||
|
count($menuItem->getChildren()) === 0
|
||||||
|
or count($menuItem->getChildren()) > 1
|
||||||
|
or ! empty($menuItem->getFirstChild()->getLabel())
|
||||||
|
) {
|
||||||
|
return $menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, find the first level where there is actual content and use that.
|
||||||
|
while (count($menuItem->getChildren()) == 1 && empty($menuItem->getFirstChild()->getLabel())) {
|
||||||
|
$menuItem = $menuItem->getFirstChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get HTML menu in unordered 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)
|
||||||
|
* @param RendererInterface|null $renderer
|
||||||
|
* @param bool $ordered
|
||||||
|
* @return string HTML <li> items
|
||||||
|
*/
|
||||||
|
public function getHtmlMenu(
|
||||||
|
string $markup,
|
||||||
|
int $topLevel = 1,
|
||||||
|
int $depth = 6,
|
||||||
|
?RendererInterface $renderer = null,
|
||||||
|
bool $ordered = false
|
||||||
|
): string {
|
||||||
|
if (! $renderer) {
|
||||||
|
$options = ['currentClass' => 'active', 'ancestorClass' => 'active_ancestor'];
|
||||||
|
$renderer = $ordered
|
||||||
|
? new OrderedListRenderer(new Matcher(), $options)
|
||||||
|
: new ListRenderer(new Matcher(), $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
$menu = $this->getMenu($markup, $topLevel, $depth);
|
||||||
|
return $renderer->render($menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get HTML menu in ordered 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)
|
||||||
|
* @param RendererInterface|null $renderer
|
||||||
|
* @return string HTML <li> items
|
||||||
|
*/
|
||||||
|
public function getOrderedHtmlMenu(
|
||||||
|
string $markup,
|
||||||
|
int $topLevel = 1,
|
||||||
|
int $depth = 6,
|
||||||
|
RendererInterface $renderer = null
|
||||||
|
): string {
|
||||||
|
return $this->getHtmlMenu($markup, $topLevel, $depth, $renderer, true);
|
||||||
|
}
|
||||||
|
}
|
74
plugins/page-toc/classes/UniqueSlugify.php
Normal file
74
plugins/page-toc/classes/UniqueSlugify.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PageTOC
|
||||||
|
*
|
||||||
|
* This plugin allows creation of Table of Contents + Link Anchors
|
||||||
|
*
|
||||||
|
* Based on the original version https://github.com/caseyamcl/toc
|
||||||
|
* by Casey McLaughlin <caseyamcl@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under MIT, see LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Grav\Plugin\PageToc;
|
||||||
|
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
use Cocur\Slugify\SlugifyInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UniqueSluggify creates slugs from text without repeating the same slug twice per instance
|
||||||
|
*/
|
||||||
|
class UniqueSlugify implements SlugifyInterface
|
||||||
|
{
|
||||||
|
protected $slugify;
|
||||||
|
protected $used;
|
||||||
|
|
||||||
|
protected $options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param SlugifyInterface|null $slugify
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->used = array();
|
||||||
|
$this->slugify = new Slugify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slugify
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @param array|null $options
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function slugify($text, $options = null): string
|
||||||
|
{
|
||||||
|
$slugged = $this->slugify->slugify($text, $options);
|
||||||
|
|
||||||
|
$maxlen = $options['maxlen'] ?? null;
|
||||||
|
$prefix = $options['prefix'] ?? null;
|
||||||
|
|
||||||
|
if (is_int($maxlen) && strlen($slugged) > $maxlen) {
|
||||||
|
$slugged = substr($slugged, 0, $maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($prefix)) {
|
||||||
|
$slugged = $prefix . $slugged;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 1;
|
||||||
|
$orig = $slugged;
|
||||||
|
while (in_array($slugged, $this->used)) {
|
||||||
|
$slugged = $orig . '-' . $count;
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->used[] = $slugged;
|
||||||
|
return $slugged;
|
||||||
|
}
|
||||||
|
}
|
51
plugins/page-toc/classes/shortcodes/AnchorShortcode.php
Normal file
51
plugins/page-toc/classes/shortcodes/AnchorShortcode.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
namespace Grav\Plugin\Shortcodes;
|
||||||
|
|
||||||
|
use Grav\Common\Inflector;
|
||||||
|
use Grav\Plugin\PageToc\UniqueSlugify;
|
||||||
|
use Grav\Plugin\PageTOCPlugin;
|
||||||
|
use Thunder\Shortcode\Shortcode\ProcessedShortcode;
|
||||||
|
|
||||||
|
class AnchorShortcode extends Shortcode
|
||||||
|
{
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->shortcode->getRawHandlers()->add('anchor', function(ProcessedShortcode $sc) {
|
||||||
|
|
||||||
|
$id = $this->cleanParam($sc->getParameter('id', $sc->getBbCode()));
|
||||||
|
$tag = $this->cleanParam($sc->getParameter('tag'));
|
||||||
|
$prefix = $this->cleanParam($sc->getParameter('prefix', PageTOCPlugin::configVar('anchors.slug_prefix')));
|
||||||
|
$class = $this->cleanParam($sc->getParameter('class', 'inline-anchor'));
|
||||||
|
$aria = PageTOCPlugin::configVar('anchors.aria');
|
||||||
|
$content = $sc->getContent();
|
||||||
|
|
||||||
|
$slugger = new UniqueSlugify();
|
||||||
|
|
||||||
|
if (is_null($id)) {
|
||||||
|
$id = $slugger->slugify(strip_tags($content));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($prefix)) {
|
||||||
|
$id = $prefix . $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tag) {
|
||||||
|
$output = "<$tag id=\"$id\" class=\"$class\">$content</$tag>";
|
||||||
|
} else {
|
||||||
|
$output = "<a id=\"$id\" href=\"#$id\" class=\"$class\" aria-label=\"$aria\">$content</a>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
});
|
||||||
|
$this->shortcode->getRawHandlers()->addAlias('#', 'anchor');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $param
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function cleanParam($param)
|
||||||
|
{
|
||||||
|
return trim(html_entity_decode($param), '"');
|
||||||
|
}
|
||||||
|
}
|
31
plugins/page-toc/composer.json
Normal file
31
plugins/page-toc/composer.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "trilbymedia/page-toc",
|
||||||
|
"type": "grav-plugin",
|
||||||
|
"description": "Page TOC plugin",
|
||||||
|
"keywords": ["plugin"],
|
||||||
|
"homepage": "https://github.com/trilbymedia/grav-plugin-page-toc",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Trilby Media",
|
||||||
|
"email": "hello@trilby.media",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"cocur/slugify": "^3.0|^4.0",
|
||||||
|
"knplabs/knp-menu": "^3.2",
|
||||||
|
"php": ">=7.3.6"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Grav\\Plugin\\PageToc\\": "classes/"
|
||||||
|
},
|
||||||
|
"classmap": ["page-toc.php"]
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"platform": {
|
||||||
|
"php": "7.3.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
170
plugins/page-toc/composer.lock
generated
Normal file
170
plugins/page-toc/composer.lock
generated
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "8a2c6e66a1071ca85fae3951c0161cee",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "cocur/slugify",
|
||||||
|
"version": "v4.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/cocur/slugify.git",
|
||||||
|
"reference": "3f1ffc300f164f23abe8b64ffb3f92d35cec8307"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/cocur/slugify/zipball/3f1ffc300f164f23abe8b64ffb3f92d35cec8307",
|
||||||
|
"reference": "3f1ffc300f164f23abe8b64ffb3f92d35cec8307",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"php": ">=7.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/config": "<3.4 || >=4,<4.3",
|
||||||
|
"symfony/dependency-injection": "<3.4 || >=4,<4.3",
|
||||||
|
"symfony/http-kernel": "<3.4 || >=4,<4.3",
|
||||||
|
"twig/twig": "<2.12.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/framework": "~5.1",
|
||||||
|
"latte/latte": "~2.2",
|
||||||
|
"league/container": "^2.2.0",
|
||||||
|
"mikey179/vfsstream": "~1.6.8",
|
||||||
|
"mockery/mockery": "^1.3",
|
||||||
|
"nette/di": "~2.4",
|
||||||
|
"phpunit/phpunit": "^5.7.27",
|
||||||
|
"pimple/pimple": "~1.1",
|
||||||
|
"plumphp/plum": "~0.1",
|
||||||
|
"symfony/config": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"symfony/dependency-injection": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"symfony/http-kernel": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"twig/twig": "^2.12.1 || ~3.0",
|
||||||
|
"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": "Florian Eckerstorfer",
|
||||||
|
"email": "florian@eckerstorfer.co",
|
||||||
|
"homepage": "https://florian.ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ivo Bathke",
|
||||||
|
"email": "ivo.bathke@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Converts a string into a slug.",
|
||||||
|
"keywords": [
|
||||||
|
"slug",
|
||||||
|
"slugify"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/cocur/slugify/issues",
|
||||||
|
"source": "https://github.com/cocur/slugify/tree/master"
|
||||||
|
},
|
||||||
|
"time": "2019-12-14T13:04:14+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "knplabs/knp-menu",
|
||||||
|
"version": "v3.3.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/KnpLabs/KnpMenu.git",
|
||||||
|
"reference": "8bd3dc2afa22c65617c563c5e25e62d6e23e98c7"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/KnpLabs/KnpMenu/zipball/8bd3dc2afa22c65617c563c5e25e62d6e23e98c7",
|
||||||
|
"reference": "8bd3dc2afa22c65617c563c5e25e62d6e23e98c7",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.3 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"twig/twig": "<1.40 || >=2,<2.9"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"psr/container": "^1.0",
|
||||||
|
"symfony/http-foundation": "^4.4 || ^5.0 || ^6.0",
|
||||||
|
"symfony/phpunit-bridge": "^5.3",
|
||||||
|
"symfony/routing": "^4.4 || ^5.0 || ^6.0",
|
||||||
|
"twig/twig": "^1.40 || ^2.9 || ^3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"twig/twig": "for the TwigRenderer and the integration with your templates"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.3-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Knp\\Menu\\": "src/Knp/Menu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "KnpLabs",
|
||||||
|
"homepage": "https://knplabs.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Christophe Coevoet",
|
||||||
|
"email": "stof@notk.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "The Community",
|
||||||
|
"homepage": "https://github.com/KnpLabs/KnpMenu/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An object oriented menu library",
|
||||||
|
"homepage": "https://knplabs.com",
|
||||||
|
"keywords": [
|
||||||
|
"menu",
|
||||||
|
"tree"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/KnpLabs/KnpMenu/issues",
|
||||||
|
"source": "https://github.com/KnpLabs/KnpMenu/tree/v3.3.0"
|
||||||
|
},
|
||||||
|
"time": "2021-10-23T15:01:04+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": ">=7.3.6"
|
||||||
|
},
|
||||||
|
"platform-dev": [],
|
||||||
|
"platform-overrides": {
|
||||||
|
"php": "7.3.6"
|
||||||
|
},
|
||||||
|
"plugin-api-version": "2.1.0"
|
||||||
|
}
|
38
plugins/page-toc/languages.yaml
Normal file
38
plugins/page-toc/languages.yaml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
de:
|
||||||
|
PLUGIN_PAGE_TOC:
|
||||||
|
TABLE_OF_CONTENTS: Inhaltsverzeichnis
|
||||||
|
en:
|
||||||
|
PLUGIN_PAGE_TOC:
|
||||||
|
TABLE_OF_CONTENTS: Table of Contents
|
||||||
|
INCLUDE_CSS: 'Include CSS'
|
||||||
|
ACTIVE_BY_DEFAULT: 'Anchors generated by default'
|
||||||
|
ACTIVE_BY_DEFAULT_PAGE: 'Anchors generated for this page'
|
||||||
|
ACTIVE_BY_DEFAULT_PAGE_HELP: 'If the default setting is disabled, you can enable on this page, or vice versa'
|
||||||
|
TOC_SECTION: 'Table of Contents Configuration'
|
||||||
|
START_TOC_HEADERS: 'Start TOC headers'
|
||||||
|
START_TOC_HEADERS_HELP: 'The Header level to start the TOC from'
|
||||||
|
DEPTH_TOC_HEADERS: 'Depth of TOC headers'
|
||||||
|
DEPTH_TOC_HEADERS_HELP: 'The number of headers levels from the ''start'' to include in the TOC'
|
||||||
|
HEADER_CSS_CLASSES: 'Header CSS classes'
|
||||||
|
HEADER_CSS_CLASSES_HELP: 'Any custom classes to add to the header tags when IDs for slugs are added'
|
||||||
|
ANCHORS_SECTION: 'Anchors Configuration'
|
||||||
|
START_ANCHOR_HEADERS: 'Start Anchor headers'
|
||||||
|
DEPTH_ANCHOR_HEADERS: 'Depth of Anchor headers'
|
||||||
|
LINK_ANCHOR_HEADERS: 'Link Anchors'
|
||||||
|
ARIA_LABEL: 'Aria Label'
|
||||||
|
ANCHORS_CLASS: 'Custom CSS classes for anchors'
|
||||||
|
ANCHORS_CLASS_HELP: 'Any custom classes to add to the anchor tags'
|
||||||
|
ANCHORS_ICON: 'Anchor icon symbol'
|
||||||
|
ANCHORS_ICON_HELP: 'Can be any text character, symbol, unicode character or even emjoi. leave blank if you intend to style with CSS.'
|
||||||
|
ANCHORS_POSITION: 'Anchor Position'
|
||||||
|
ANCHORS_POSITION_HELP: 'Position to put the anchor, `before|after`'
|
||||||
|
ANCHORS_COPY_TO_CLIPBOARD: 'Copy to Clipboard'
|
||||||
|
ANCHORS_COPY_TO_CLIPBOARD_HELP: 'When clicking an anchor, it will also copy to clipboard the full URL. Convenient for sharing/opening in new tab'
|
||||||
|
BEFORE_TEXT: 'Before text'
|
||||||
|
AFTER_TEXT: 'After text'
|
||||||
|
SLUG_MAXLEN: 'Slug max-length'
|
||||||
|
SLUG_MAXLEN_HELP: 'Max length of slugs used for anchors'
|
||||||
|
SLUG_PREFIX: 'Slug prefix'
|
||||||
|
SLUG_PREFIX_HELP: 'A prefix used in front of generated slugs'
|
||||||
|
PAGE_ANCHORS_SECTION: 'Page Anchors Configuration'
|
||||||
|
COPY_TO_CLIPBOARD: 'Copy to Clipboard'
|
222
plugins/page-toc/page-toc.php
Normal file
222
plugins/page-toc/page-toc.php
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
<?php
|
||||||
|
namespace Grav\Plugin;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Grav\Common\Data;
|
||||||
|
use Grav\Common\Grav;
|
||||||
|
use Grav\Common\Page\Interfaces\PageInterface;
|
||||||
|
use Grav\Common\Plugin;
|
||||||
|
use Grav\Common\Utils;
|
||||||
|
use Grav\Plugin\PageToc\MarkupFixer;
|
||||||
|
use Grav\Plugin\PageToc\TocGenerator;
|
||||||
|
use RocketTheme\Toolbox\Event\Event;
|
||||||
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PageTOCPlugin
|
||||||
|
* @package Grav\Plugin
|
||||||
|
*/
|
||||||
|
class PageTOCPlugin extends Plugin
|
||||||
|
{
|
||||||
|
protected $start;
|
||||||
|
protected $end;
|
||||||
|
protected $toc_regex = '#\[TOC\s*\/?\]#i';
|
||||||
|
|
||||||
|
protected $fixer;
|
||||||
|
protected $generator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* The getSubscribedEvents() gives the core a list of events
|
||||||
|
* that the plugin wants to listen to. The key of each
|
||||||
|
* array section is the event that the plugin listens to
|
||||||
|
* and the value (in the form of an array) contains the
|
||||||
|
* callable (or function) as well as the priority. The
|
||||||
|
* higher the number the higher the priority.
|
||||||
|
*/
|
||||||
|
public static function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'onPluginsInitialized' => ['onPluginsInitialized', 0]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composer autoload
|
||||||
|
*
|
||||||
|
* @return ClassLoader
|
||||||
|
*/
|
||||||
|
public function autoload(): ClassLoader
|
||||||
|
{
|
||||||
|
return require __DIR__ . '/vendor/autoload.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the plugin
|
||||||
|
*/
|
||||||
|
public function onPluginsInitialized()
|
||||||
|
{
|
||||||
|
// Don't proceed if we are in the admin plugin
|
||||||
|
if ($this->isAdmin()) {
|
||||||
|
$this->enable([
|
||||||
|
'onBlueprintCreated' => ['onBlueprintCreated', 0],
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the main event we are interested in
|
||||||
|
$this->enable([
|
||||||
|
'onShortcodeHandlers' => ['onShortcodeHandlers', 0],
|
||||||
|
'onTwigInitialized' => ['onTwigInitialized', 0],
|
||||||
|
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
|
||||||
|
'onTwigSiteVariables' => ['onTwigSiteVariables', 0],
|
||||||
|
'onPageContentProcessed' => ['onPageContentProcessed', -20],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onShortcodeHandlers()
|
||||||
|
{
|
||||||
|
$this->grav['shortcode']->registerAllShortcodes(__DIR__ . '/classes/shortcodes');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onPageContentProcessed(Event $event)
|
||||||
|
{
|
||||||
|
/** @var PageInterface $page */
|
||||||
|
$page = $event['page'];
|
||||||
|
|
||||||
|
$content = $page->content();
|
||||||
|
$shortcode_exists = preg_match($this->toc_regex, $content);
|
||||||
|
$active = $this->configVar('active', $page, false);
|
||||||
|
|
||||||
|
// Set ID anchors if needed
|
||||||
|
if ($active || $shortcode_exists) {
|
||||||
|
$this->registerTwigFunctions();
|
||||||
|
$markup_fixer = new MarkupFixer();
|
||||||
|
$content = $markup_fixer->fix($content, $this->getAnchorOptions($page));
|
||||||
|
$page->setRawContent($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace shortcode if found
|
||||||
|
if ($shortcode_exists) {
|
||||||
|
$toc = $this->grav['twig']->processTemplate('components/page-toc.html.twig', ['page' => $page, 'active' => true]);
|
||||||
|
$content = preg_replace($this->toc_regex, $toc, $content);
|
||||||
|
$page->setRawContent($content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onTwigInitialized()
|
||||||
|
{
|
||||||
|
$this->registerTwigFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onTwigSiteVariables()
|
||||||
|
{
|
||||||
|
if ($this->grav['config']->get('plugins.page-toc.include_css')) {
|
||||||
|
$this->grav['assets']->addCss('plugin://page-toc/assets/page-toc-anchors.css');
|
||||||
|
}
|
||||||
|
if ($this->grav['config']->get('plugins.page-toc.anchors.copy_to_clipboard')) {
|
||||||
|
$this->grav['assets']->addJs('plugin://page-toc/assets/page-toc-anchors.js', ['group' => 'bottom', 'defer' => 'defer']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerTwigFunctions()
|
||||||
|
{
|
||||||
|
static $functions_registered;
|
||||||
|
|
||||||
|
if ($functions_registered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->generator = new TocGenerator();
|
||||||
|
$this->fixer = new MarkupFixer();
|
||||||
|
$twig = $this->grav['twig']->twig();
|
||||||
|
|
||||||
|
$twig->addFunction(new TwigFunction('toc', function ($markup, $start = null, $depth = null) {
|
||||||
|
$options = $this->getTocOptions(null, $start, $depth);
|
||||||
|
return $this->generator->getHtmlMenu($markup, $options['start'], $options['depth']);
|
||||||
|
}, ['is_safe' => ['html']]));
|
||||||
|
|
||||||
|
$twig->addFunction(new TwigFunction('toc_ordered', function ($markup, $start = null, $depth = null) {
|
||||||
|
$options = $this->getTocOptions(null, $start, $depth);
|
||||||
|
return $this->generator->getHtmlMenu($markup, $options['start'], $options['depth'], null, true);
|
||||||
|
}, ['is_safe' => ['html']]));
|
||||||
|
|
||||||
|
$twig->addFunction(new TwigFunction('toc_items', function ($markup, $start = null, $depth = null) {
|
||||||
|
$options = $this->getTocOptions(null, $start, $depth);
|
||||||
|
return $this->generator->getMenu($markup, $options['start'], $options['depth']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
$twig->addFunction(new TwigFunction('add_anchors', function ($markup, $start = null, $depth = null) {
|
||||||
|
$options = $this->getAnchorOptions(null, $start, $depth);
|
||||||
|
return $this->fixer->fix($markup, $options);
|
||||||
|
}, ['is_safe' => ['html']]));
|
||||||
|
|
||||||
|
$twig->addFunction(new TwigFunction('toc_config_var', function ($var) {
|
||||||
|
return static::configVar($var);
|
||||||
|
}));
|
||||||
|
|
||||||
|
$functions_registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onTwigTemplatePaths()
|
||||||
|
{
|
||||||
|
$this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend page blueprints with TOC options.
|
||||||
|
*
|
||||||
|
* @param Event $event
|
||||||
|
*/
|
||||||
|
public function onBlueprintCreated(Event $event)
|
||||||
|
{
|
||||||
|
static $inEvent = false;
|
||||||
|
|
||||||
|
/** @var Data\Blueprint $blueprint */
|
||||||
|
$blueprint = $event['blueprint'];
|
||||||
|
$form = $blueprint->form();
|
||||||
|
|
||||||
|
$advanced_tab_exists = isset($form['fields']['tabs']['fields']['advanced']);
|
||||||
|
|
||||||
|
if (!$inEvent && $advanced_tab_exists) {
|
||||||
|
$inEvent = true;
|
||||||
|
$blueprints = new Data\Blueprints(__DIR__ . '/blueprints/');
|
||||||
|
$extends = $blueprints->get('page-toc');
|
||||||
|
$blueprint->extend($extends, true);
|
||||||
|
$inEvent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTocOptions(PageInterface $page = null, $start = null, $depth = null): array
|
||||||
|
{
|
||||||
|
$page = $page ?? $this->grav['page'];
|
||||||
|
return [
|
||||||
|
'start' => $start ?? $this->configVar('start', $page,1),
|
||||||
|
'depth' => $depth ?? $this->configVar('depth', $page,6),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getAnchorOptions(PageInterface $page = null, $start = null, $depth = null): array
|
||||||
|
{
|
||||||
|
$page = $page ?? $this->grav['page'];
|
||||||
|
return [
|
||||||
|
'start' => (int) ($start ?? $this->configVar('anchors.start', $page,1)),
|
||||||
|
'depth' => (int) ($depth ?? $this->configVar('anchors.depth', $page,6)),
|
||||||
|
'hclass' => $this->configVar('hclass', $page,null),
|
||||||
|
'link' => $this->configVar('anchors.link', $page,true),
|
||||||
|
'position' => $this->configVar('anchors.position', $page,'before'),
|
||||||
|
'aria' => $this->configVar('anchors.aria', $page,'Anchor'),
|
||||||
|
'icon' => $this->configVar('anchors.icon', $page,'#'),
|
||||||
|
'class' => $this->configVar('anchors.class', $page,null),
|
||||||
|
'maxlen' => (int) ($this->configVar('anchors.slug_maxlen', $page,null)),
|
||||||
|
'prefix' => $this->configVar('anchors.slug_prefix', $page,null),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function configVar($var, $page = null, $default = null)
|
||||||
|
{
|
||||||
|
return Plugin::inheritedConfigOption('page-toc', $var, $page, $default);
|
||||||
|
}
|
||||||
|
}
|
17
plugins/page-toc/page-toc.yaml
Normal file
17
plugins/page-toc/page-toc.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
enabled: true # Plugin enabled
|
||||||
|
include_css: true # Include CSS
|
||||||
|
active: true # Anchor IDs processed and generated for all pages
|
||||||
|
start: 1 # Start header tag level (1 = h1) for TOC
|
||||||
|
depth: 6 # Depth from start (2 = 2 levels deep) for TOC
|
||||||
|
hclass: # Custom Header TOC styling classes
|
||||||
|
anchors: # Anchor configuration
|
||||||
|
start: 1 # Start header tag level (1 = h1)
|
||||||
|
depth: 6 # Depth from start (2 = 2 levels deep)
|
||||||
|
link: true # Enabled auto-generation of clickable link with fragment
|
||||||
|
aria: Anchor # Aria label to use
|
||||||
|
class: # Custom Header anchor styling classes
|
||||||
|
icon: '#' # Icon to use, can be a symbol, emoji, ascii etc.
|
||||||
|
position: after # Position to put the anchor, `before|after`
|
||||||
|
copy_to_clipboard: false # Copy to clipboard functionality
|
||||||
|
slug_maxlen: 25 # Max length of slugs used for anchors
|
||||||
|
slug_prefix: # A prefix used in front of generated slugs
|
26
plugins/page-toc/templates/components/page-toc.html.twig
Normal file
26
plugins/page-toc/templates/components/page-toc.html.twig
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{% macro toc_loop(items) %}
|
||||||
|
{% import _self as self %}
|
||||||
|
{% for item in items %}
|
||||||
|
{% set class = loop.first ? 'first' : loop.last ? 'last' : null %}
|
||||||
|
<li {% if class %}class="{{ class }}"{% endif %}>
|
||||||
|
<a href="{{ item.uri }}">{{ item.label }}</a>
|
||||||
|
{% if item.children|length > 0 %}
|
||||||
|
<ul>
|
||||||
|
{{ _self.toc_loop(item.children) }}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% if active or toc_config_var('active') %}
|
||||||
|
<div class="page-toc">
|
||||||
|
{% set table_of_contents = toc_items(page.content) %}
|
||||||
|
{% if table_of_contents is not empty %}
|
||||||
|
<h4>{{ 'PLUGIN_PAGE_TOC.TABLE_OF_CONTENTS'|t }}</h4>
|
||||||
|
<ul>
|
||||||
|
{{ _self.toc_loop(table_of_contents.children) }}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
7
plugins/page-toc/vendor/autoload.php
vendored
Normal file
7
plugins/page-toc/vendor/autoload.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit88b09b7fda3e99f2e22346b58205e375::getLoader();
|
9
plugins/page-toc/vendor/cocur/slugify/LICENSE
vendored
Normal file
9
plugins/page-toc/vendor/cocur/slugify/LICENSE
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-217 Florian Eckerstorfer
|
||||||
|
|
||||||
|
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.
|
751
plugins/page-toc/vendor/cocur/slugify/README.md
vendored
Normal file
751
plugins/page-toc/vendor/cocur/slugify/README.md
vendored
Normal file
@ -0,0 +1,751 @@
|
|||||||
|
# cocur/slugify
|
||||||
|
|
||||||
|
> Converts a string into a slug.
|
||||||
|
|
||||||
|
[![Build Status](https://img.shields.io/travis/cocur/slugify.svg?style=flat)](https://travis-ci.org/cocur/slugify)
|
||||||
|
[![Windows Build status](https://ci.appveyor.com/api/projects/status/9yv498ff61byp742?svg=true)](https://ci.appveyor.com/project/florianeckerstorfer/slugify)
|
||||||
|
[![Scrutinizer Quality Score](https://img.shields.io/scrutinizer/g/cocur/slugify.svg?style=flat)](https://scrutinizer-ci.com/g/cocur/slugify/)
|
||||||
|
[![Code Coverage](https://scrutinizer-ci.com/g/cocur/slugify/badges/coverage.png?b=master&style=flat-square)](https://scrutinizer-ci.com/g/cocur/slugify/?branch=master)
|
||||||
|
|
||||||
|
[![Latest Release](https://img.shields.io/packagist/v/cocur/slugify.svg)](https://packagist.org/packages/cocur/slugify)
|
||||||
|
[![MIT License](https://img.shields.io/packagist/l/cocur/slugify.svg)](http://opensource.org/licenses/MIT)
|
||||||
|
[![Total Downloads](https://img.shields.io/packagist/dt/cocur/slugify.svg)](https://packagist.org/packages/cocur/slugify)
|
||||||
|
|
||||||
|
Developed by [Florian Eckerstorfer](https://florian.ec) in Vienna, Europe with the help of
|
||||||
|
[many great contributors](https://github.com/cocur/slugify/graphs/contributors).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Removes all special characters from a string.
|
||||||
|
- Provides custom replacements for Arabic, Austrian, Azerbaijani, Brazilian Portuguese, Bulgarian, Burmese, Chinese, Croatian, Czech, Esperanto, Estonian, Finnish, French, Georgian, German, Greek, Hindi, Hungarian, Italian, Latvian, Lithuanian, Macedonian, Norwegian, Polish, Romanian, Russian, Serbian, Spanish, Swedish, Turkish, Ukrainian and Vietnamese special characters. Instead of removing these characters, Slugify approximates them (e.g., `ae` replaces `ä`).
|
||||||
|
- No external dependencies.
|
||||||
|
- PSR-4 compatible.
|
||||||
|
- Compatible with PHP >= 7.
|
||||||
|
- Integrations for [Symfony (3, 4 and 5)](http://symfony.com), [Laravel](http://laravel.com), [Twig (2 and 3)](http://twig.sensiolabs.org), [Zend Framework 2](http://framework.zend.com/), [Nette Framework](http://nette.org/), [Latte](http://latte.nette.org/) and [Plum](https://github.com/plumphp/plum).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
You can install Slugify through [Composer](https://getcomposer.org):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ composer require cocur/slugify
|
||||||
|
```
|
||||||
|
|
||||||
|
Slugify requires the Multibyte String extension from PHP. Typically you can use the configure option `--enable-mbstring` while compiling PHP. More information can be found in the [PHP documentation](http://php.net/manual/en/mbstring.installation.php).
|
||||||
|
|
||||||
|
Further steps may be needed for [integrations](#integrations).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Generate a slug:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
|
||||||
|
$slugify = new Slugify();
|
||||||
|
echo $slugify->slugify('Hello World!'); // hello-world
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also change the separator used by `Slugify`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
echo $slugify->slugify('Hello World!', '_'); // hello_world
|
||||||
|
```
|
||||||
|
|
||||||
|
The library also contains `Cocur\Slugify\SlugifyInterface`. Use this interface whenever you need to type hint an
|
||||||
|
instance of `Slugify`.
|
||||||
|
|
||||||
|
To add additional transliteration rules you can use the `addRule()` method.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify->addRule('i', 'ey');
|
||||||
|
echo $slugify->slugify('Hi'); // hey
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rulesets
|
||||||
|
|
||||||
|
Many of the transliterations rules used in Slugify are specific to a language. These rules are therefore categorized
|
||||||
|
using rulesets. Rules for the most popular are activated by default in a specific order. You can change which rulesets
|
||||||
|
are activated and the order in which they are activated. The order is important when there are conflicting rules in
|
||||||
|
different languages. For example, in German `ä` is transliterated with `ae`, in Turkish the correct transliteration is
|
||||||
|
`a`. By default the German transliteration is used since German is used more often on the internet. If you want to use
|
||||||
|
prefer the Turkish transliteration you have to possibilities. You can activate it after creating the constructor:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify();
|
||||||
|
$slugify->slugify('ä'); // -> "ae"
|
||||||
|
$slugify->activateRuleSet('turkish');
|
||||||
|
$slugify->slugify('ä'); // -> "a"
|
||||||
|
```
|
||||||
|
|
||||||
|
An alternative way would be to pass the rulesets and their order to the constructor.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify(['rulesets' => ['default', 'turkish']]);
|
||||||
|
$slugify->slugify('ä'); // -> "a"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find a list of the available rulesets in [Resources/rules](https://github.com/cocur/slugify/tree/master/Resources/rules).
|
||||||
|
|
||||||
|
### More options
|
||||||
|
|
||||||
|
The constructor takes an options array, you have already seen the `rulesets` options above. You can also change the
|
||||||
|
regular expression that is used to replace characters with the separator.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify(['regexp' => '/([^A-Za-z0-9]|-)+/']);
|
||||||
|
```
|
||||||
|
|
||||||
|
_(The regular expression used in the example above is the default one.)_
|
||||||
|
|
||||||
|
By default Slugify will convert the slug to lowercase. If you want to preserve the case of the string you can set the
|
||||||
|
`lowercase` option to false.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify(['lowercase' => false]);
|
||||||
|
$slugify->slugify('Hello World'); // -> "Hello-World"
|
||||||
|
```
|
||||||
|
|
||||||
|
Lowercasing is done before using the regular expression. If you want to keep the lowercasing behavior but your regular
|
||||||
|
expression needs to match uppercase letters, you can set the `lowercase_after_regexp` option to `true`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify([
|
||||||
|
'regexp' => '/(?<=[[:^upper:]])(?=[[:upper:]])/',
|
||||||
|
'lowercase_after_regexp' => false,
|
||||||
|
]);
|
||||||
|
$slugify->slugify('FooBar'); // -> "foo-bar"
|
||||||
|
```
|
||||||
|
|
||||||
|
By default Slugify will use dashes as separators. If you want to use a different default separator, you can set the
|
||||||
|
`separator` option.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify(['separator' => '_']);
|
||||||
|
$slugify->slugify('Hello World'); // -> "hello_world"
|
||||||
|
```
|
||||||
|
|
||||||
|
By default Slugify will remove leading and trailing separators before returning the slug. If you do not want the slug to
|
||||||
|
be trimmed you can set the `trim` option to false.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify(['trim' => false]);
|
||||||
|
$slugify->slugify('Hello World '); // -> "hello-world-"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Changing options on the fly
|
||||||
|
|
||||||
|
You can overwrite any of the above options on the fly by passing an options array as second argument to the `slugify()`
|
||||||
|
method. For example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify();
|
||||||
|
$slugify->slugify('Hello World', ['lowercase' => false]); // -> "Hello-World"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also modify the separator this way:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify();
|
||||||
|
$slugify->slugify('Hello World', ['separator' => '_']); // -> "hello_world"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can even activate a custom ruleset without touching the default rules:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slugify = new Slugify();
|
||||||
|
$slugify->slugify('für', ['ruleset' => 'turkish']); // -> "fur"
|
||||||
|
$slugify->slugify('für'); // -> "fuer"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
|
||||||
|
We really appreciate if you report bugs and errors in the transliteration, especially if you are a native speaker of
|
||||||
|
the language and question. Feel free to ask for additional languages in the issues, but please note that the
|
||||||
|
maintainer of this repository does not speak all languages. If you can provide a Pull Request with rules for
|
||||||
|
a new language or extend the rules for an existing language that would be amazing.
|
||||||
|
|
||||||
|
To add a new language you need to:
|
||||||
|
|
||||||
|
1. Create a `[language].json` in `Resources/rules`
|
||||||
|
2. If you believe the language should be a default ruleset you can add the language to
|
||||||
|
`Cocur\Slugify\Slugify::$options`. If you add the language there all existing tests still have to pass
|
||||||
|
3. Run `php bin/generate-default.php`
|
||||||
|
4. Add tests for the language in `tests/SlugifyTest.php`. If the language is in the default ruleset add your
|
||||||
|
test cases to `defaultRuleProvider()`, otherwise to `customRulesProvider()`.
|
||||||
|
|
||||||
|
Submit PR. Thank you very much. 💚
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
The full Code of Conduct can be found [here](https://github.com/cocur/slugify/blob/master/CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
This project is no place for hate. If you have any problems please contact Florian: [florian@eckerstorfer.net](mailto:florian@eckerstorfer.net) ✌🏻🏳️🌈
|
||||||
|
|
||||||
|
### Further information
|
||||||
|
|
||||||
|
- [API docs](http://cocur.co/slugify/api/master/)
|
||||||
|
|
||||||
|
## Integrations
|
||||||
|
|
||||||
|
### Symfony
|
||||||
|
|
||||||
|
Slugify contains a Symfony bundle and service definition that allow you to use it as a service in your Symfony application. The code resides in `Cocur\Slugify\Bridge\Symfony\CocurSlugifyBundle` and you only need to activate it:
|
||||||
|
|
||||||
|
#### Symfony 2
|
||||||
|
|
||||||
|
Support for Symfony 2 has been dropped in Slugify 4.0.0, use `cocur/slugify@3`.
|
||||||
|
|
||||||
|
#### Symfony 3
|
||||||
|
|
||||||
|
```php
|
||||||
|
// app/AppKernel.php
|
||||||
|
|
||||||
|
class AppKernel extends Kernel
|
||||||
|
{
|
||||||
|
public function registerBundles()
|
||||||
|
{
|
||||||
|
$bundles = array(
|
||||||
|
// ...
|
||||||
|
new Cocur\Slugify\Bridge\Symfony\CocurSlugifyBundle(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Symfony >= 4
|
||||||
|
|
||||||
|
```php
|
||||||
|
// config/bundles.php
|
||||||
|
|
||||||
|
return [
|
||||||
|
// ...
|
||||||
|
Cocur\Slugify\Bridge\Symfony\CocurSlugifyBundle::class => ['all' => true],
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now use the `cocur_slugify` service everywhere in your application, for example, in your controller:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slug = $this->get('cocur_slugify')->slugify('Hello World!');
|
||||||
|
```
|
||||||
|
|
||||||
|
The bundle also provides an alias `slugify` for the `cocur_slugify` service:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$slug = $this->get('slugify')->slugify('Hello World!');
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use `autowire` (Symfony >=3.3), you can inject it into your services like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function __construct(\Cocur\Slugify\SlugifyInterface $slugify)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the following configuration settings in `config.yml` to adjust the slugify service:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cocur_slugify:
|
||||||
|
lowercase: <boolean>
|
||||||
|
separator: <string>
|
||||||
|
regexp: <string>
|
||||||
|
rulesets: {} # List of rulesets: https://github.com/cocur/slugify/tree/master/Resources/rules
|
||||||
|
```
|
||||||
|
|
||||||
|
### Twig
|
||||||
|
|
||||||
|
If you use the Symfony framework with Twig you can use the Twig filter `slugify` in your templates after you have setup
|
||||||
|
Symfony integrations (see above).
|
||||||
|
|
||||||
|
```twig
|
||||||
|
{{ 'Hällo Wörld'|slugify }}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use Twig outside of the Symfony framework you first need to add the extension to your environment:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Cocur\Slugify\Bridge\Twig\SlugifyExtension;
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
|
||||||
|
$twig = new Twig_Environment($loader);
|
||||||
|
$twig->addExtension(new SlugifyExtension(Slugify::create()));
|
||||||
|
```
|
||||||
|
|
||||||
|
To use the Twig filter with [TwigBridge](https://github.com/rcrowe/TwigBridge) for Laravel, you'll need to add the
|
||||||
|
Slugify extension using a closure:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// laravel/app/config/packages/rcrowe/twigbridge/config.php
|
||||||
|
|
||||||
|
'extensions' => array(
|
||||||
|
//...
|
||||||
|
function () {
|
||||||
|
return new \Cocur\Slugify\Bridge\Twig\SlugifyExtension(\Cocur\Slugify\Slugify::create());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find more information about registering extensions in the
|
||||||
|
[Twig documentation](http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension).
|
||||||
|
|
||||||
|
### Mustache.php
|
||||||
|
|
||||||
|
We don't need an additional integration to use Slugify in [Mustache.php](https://github.com/bobthecow/mustache.php).
|
||||||
|
If you want to use Slugify in Mustache, just add a helper:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
|
||||||
|
$mustache = new Mustache_Engine(array(
|
||||||
|
// ...
|
||||||
|
'helpers' => array('slugify' => function($string, $separator = null) {
|
||||||
|
return Slugify::create()->slugify($string, $separator);
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Laravel
|
||||||
|
|
||||||
|
Slugify also provides a service provider to integrate into Laravel (versions 4.1 and later).
|
||||||
|
|
||||||
|
In your Laravel project's `app/config/app.php` file, add the service provider into the "providers" array:
|
||||||
|
|
||||||
|
```php
|
||||||
|
'providers' => array(
|
||||||
|
"Cocur\Slugify\Bridge\Laravel\SlugifyServiceProvider",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
And add the facade into the "aliases" array:
|
||||||
|
|
||||||
|
```php
|
||||||
|
'aliases' => array(
|
||||||
|
"Slugify" => "Cocur\Slugify\Bridge\Laravel\SlugifyFacade",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then use the `Slugify::slugify()` method in your controllers:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$url = Slugify::slugify('welcome to the homepage');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Zend Framework 2
|
||||||
|
|
||||||
|
Slugify can be easely used in Zend Framework 2 applications. Included bridge provides a service and a view helper
|
||||||
|
already registered for you.
|
||||||
|
|
||||||
|
Just enable the module in your configuration like this.
|
||||||
|
|
||||||
|
```php
|
||||||
|
return array(
|
||||||
|
//...
|
||||||
|
|
||||||
|
'modules' => array(
|
||||||
|
'Application',
|
||||||
|
'ZfcBase',
|
||||||
|
'Cocur\Slugify\Bridge\ZF2' // <- Add this line
|
||||||
|
//...
|
||||||
|
)
|
||||||
|
|
||||||
|
//...
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
After that you can retrieve the `Cocur\Slugify\Slugify` service (or the `slugify` alias) and generate a slug.
|
||||||
|
|
||||||
|
```php
|
||||||
|
/** @var \Zend\ServiceManager\ServiceManager $sm */
|
||||||
|
$slugify = $sm->get('Cocur\Slugify\Slugify');
|
||||||
|
$slug = $slugify->slugify('Hällo Wörld');
|
||||||
|
$anotherSlug = $slugify->slugify('Hällo Wörld', '_');
|
||||||
|
```
|
||||||
|
|
||||||
|
In your view templates use the `slugify` helper to generate slugs.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php echo $this->slugify('Hällo Wörld') ?>
|
||||||
|
<?php echo $this->slugify('Hällo Wörld', '_') ?>
|
||||||
|
```
|
||||||
|
|
||||||
|
The service (which is also used in the view helper) can be customized by defining this configuration key.
|
||||||
|
|
||||||
|
```php
|
||||||
|
return array(
|
||||||
|
'cocur_slugify' => array(
|
||||||
|
'reg_exp' => '/([^a-zA-Z0-9]|-)+/'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nette Framework
|
||||||
|
|
||||||
|
Slugify contains a Nette extension that allows you to use it as a service in your Nette application. You only need to
|
||||||
|
register it in your `config.neon`:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# app/config/config.neon
|
||||||
|
|
||||||
|
extensions:
|
||||||
|
slugify: Cocur\Slugify\Bridge\Nette\SlugifyExtension
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now use the `Cocur\Slugify\SlugifyInterface` service everywhere in your application, for example in your
|
||||||
|
presenter:
|
||||||
|
|
||||||
|
```php
|
||||||
|
class MyPresenter extends \Nette\Application\UI\Presenter
|
||||||
|
{
|
||||||
|
/** @var \Cocur\Slugify\SlugifyInterface @inject */
|
||||||
|
public $slugify;
|
||||||
|
|
||||||
|
public function renderDefault()
|
||||||
|
{
|
||||||
|
$this->template->hello = $this->slugify->slugify('Hällo Wörld');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Latte
|
||||||
|
|
||||||
|
If you use the Nette Framework with it's native Latte templating engine, you can use the Latte filter `slugify` in your
|
||||||
|
templates after you have setup Nette extension (see above).
|
||||||
|
|
||||||
|
```smarty
|
||||||
|
{$hello|slugify}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use Latte outside of the Nette Framework you first need to add the filter to your engine:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Cocur\Slugify\Bridge\Latte\SlugifyHelper;
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
use Latte;
|
||||||
|
|
||||||
|
$latte = new Latte\Engine();
|
||||||
|
$latte->addFilter('slugify', array(new SlugifyHelper(Slugify::create()), 'slugify'));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slim 3
|
||||||
|
|
||||||
|
Slugify does not need a specific bridge to work with [Slim 3](http://www.slimframework.com), just add the following configuration:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$container['view'] = function ($c) {
|
||||||
|
$settings = $c->get('settings');
|
||||||
|
$view = new \Slim\Views\Twig($settings['view']['template_path'], $settings['view']['twig']);
|
||||||
|
$view->addExtension(new Slim\Views\TwigExtension($c->get('router'), $c->get('request')->getUri()));
|
||||||
|
$view->addExtension(new Cocur\Slugify\Bridge\Twig\SlugifyExtension(Cocur\Slugify\Slugify::create()));
|
||||||
|
return $view;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In a template you can use it like this:
|
||||||
|
|
||||||
|
```twig
|
||||||
|
<a href="/blog/{{ post.title|slugify }}">{{ post.title|raw }}</a></h5>
|
||||||
|
```
|
||||||
|
|
||||||
|
### League
|
||||||
|
|
||||||
|
Slugify provides a service provider for use with `league/container`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Cocur\Slugify;
|
||||||
|
use League\Container;
|
||||||
|
|
||||||
|
/* @var Container\ContainerInterface $container */
|
||||||
|
$container->addServiceProvider(new Slugify\Bridge\League\SlugifyServiceProvider());
|
||||||
|
|
||||||
|
/* @var Slugify\Slugify $slugify */
|
||||||
|
$slugify = $container->get(Slugify\SlugifyInterface::class);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can configure it by sharing the required options:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Cocur\Slugify;
|
||||||
|
use League\Container;
|
||||||
|
|
||||||
|
/* @var Container\ContainerInterface $container */
|
||||||
|
$container->share('config.slugify.options', [
|
||||||
|
'lowercase' => false,
|
||||||
|
'rulesets' => [
|
||||||
|
'default',
|
||||||
|
'german',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$container->addServiceProvider(new Slugify\Bridge\League\SlugifyServiceProvider());
|
||||||
|
|
||||||
|
/* @var Slugify\Slugify $slugify */
|
||||||
|
$slugify = $container->get(Slugify\SlugifyInterface::class);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can configure which rule provider to use by sharing it:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Cocur\Slugify;
|
||||||
|
use League\Container;
|
||||||
|
|
||||||
|
/* @var Container\ContainerInterface $container */
|
||||||
|
$container->share(Slugify\RuleProvider\RuleProviderInterface::class, function () {
|
||||||
|
return new Slugify\RuleProvider\FileRuleProvider(__DIR__ . '/../../rules');
|
||||||
|
]);
|
||||||
|
|
||||||
|
$container->addServiceProvider(new Slugify\Bridge\League\SlugifyServiceProvider());
|
||||||
|
|
||||||
|
/* @var Slugify\Slugify $slugify */
|
||||||
|
$slugify = $container->get(Slugify\SlugifyInterface::class);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
### Version 4.0 (14 December 2019)
|
||||||
|
|
||||||
|
Version 4 does not introduce new major features, but adds support for Symfony 4 and 5, Twig 3 and, most importantly, PHP 7.3 and 7.4.
|
||||||
|
|
||||||
|
Support for PHP 5, Twig 1 and Silex is dropped.
|
||||||
|
|
||||||
|
- [#230](https://github.com/cocur/slugify/pull/230) Add Slovak rules (by [bartko-s](https://github.com/bartko-s))
|
||||||
|
- [#236](https://github.com/cocur/slugify/pull/236) Make Twig Bridge compatible with Twig 3.0 (by [mhujer](https://github.com/mhujer))
|
||||||
|
- [#237](https://github.com/cocur/slugify/pull/237) Fix Travis CI configuration (by [kubawerlos](https://github.com/kubawerlos))
|
||||||
|
- [#238](https://github.com/cocur/slugify/pull/238) Drop Twig 1 support (by [FabienPapet](https://github.com/FabienPapet))
|
||||||
|
- [#239](https://github.com/cocur/slugify/pull/239) Fix AppVeyor (by [kubawerlos](https://github.com/kubawerlos))
|
||||||
|
- [#241](https://github.com/cocur/slugify/pull/241) Update .gitattributes (by [kubawerlos](https://github.com/kubawerlos))
|
||||||
|
- [#242](https://github.com/cocur/slugify/pull/242) Add PHP CS Fixer (by [kubawerlos](https://github.com/kubawerlos))
|
||||||
|
- [#243](https://github.com/cocur/slugify/pull/243) Normalize composer.json (by [kubawerlos](https://github.com/kubawerlos))
|
||||||
|
- [#246](https://github.com/cocur/slugify/pull/246) Add support for PHP 7.3 and 7.4 (by [snapshotpl](https://github.com/snapshotpl))
|
||||||
|
- [#247](https://github.com/cocur/slugify/pull/247) AppVeyor improvements (by [kubawerlos](https://github.com/kubawerlos))
|
||||||
|
- [#249](https://github.com/cocur/slugify/pull/249) PHPUnit annotations should be a FQCNs including a root namespace (by [kubawerlos](https://github.com/kubawerlos))
|
||||||
|
- [#250](https://github.com/cocur/slugify/pull/250) Add support for Symfony 4 and 5 (by [franmomu](https://github.com/franmomu))
|
||||||
|
- [#251](https://github.com/cocur/slugify/pull/251) Dropping support for PHP 5 (by [franmomu](https://github.com/franmomu))
|
||||||
|
- [#253](https://github.com/cocur/slugify/pull/253) Add conflict for unmaintained Symfony versions (by [franmomu](https://github.com/franmomu))
|
||||||
|
|
||||||
|
### Version 3.2 (31 January 2019)
|
||||||
|
|
||||||
|
- [#201](https://github.com/cocur/slugify/pull/201) Add strip_tags option (by [thewilkybarkid](https://github.com/thewilkybarkid))
|
||||||
|
- [#212](https://github.com/cocur/slugify/pull/212) Fix Macedonian Dze (by [franmomu](https://github.com/franmomu))
|
||||||
|
- [#213](https://github.com/cocur/slugify/pull/213) Add support for Turkmen (by [umbarov](https://github.com/umbarov))
|
||||||
|
- [#216](https://github.com/cocur/slugify/pull/216) Add lowercase_after_regexp option (by [julienfalque](https://github.com/julienfalque))
|
||||||
|
- [#217](https://github.com/cocur/slugify/pull/217) Simplify default regular impression (by [julienfalque](https://github.com/julienfalque))
|
||||||
|
- [#220](https://github.com/cocur/slugify/pull/220) Fix deprecation warning for symfony/config 4.2+ (by [franmomu](https://github.com/franmomu))
|
||||||
|
- [#221](https://github.com/cocur/slugify/pull/221) Add suuport Armenian (by [boolfalse](https://github.com/boolfalse))
|
||||||
|
|
||||||
|
### Version 3.1 (22 January 2018)
|
||||||
|
|
||||||
|
- [#195](https://github.com/cocur/slugify/pull/195) Add support for Chinese (Pinyin) (by [SuN-80](https://github.com/SuN-80), [franmomu](https://github.com/franmomu))
|
||||||
|
- [#189](https://github.com/cocur/slugify/pull/189) Add trim option (by [sforsberg](https://github.com/sforsberg))
|
||||||
|
|
||||||
|
### Version 3.0.1 (24 September 2017)
|
||||||
|
|
||||||
|
- [#183](https://github.com/cocur/slugify/pull/183) Fix invalid JSON ([RusiPapazov](https://github.com/RusiPapazov))
|
||||||
|
- [#185](https://github.com/cocur/slugify/pull/185) Fix support for Symfony > 3.3 (by [FabienPapet](https://github.com/FabienPapet))
|
||||||
|
- [#186](https://github.com/cocur/slugify/pull/186) Require Multibyte extension in `composer.json` (by [wandersonwhcr](https://github.com/wandersonwhcr))
|
||||||
|
|
||||||
|
### Version 3.0 (11 August 2017)
|
||||||
|
|
||||||
|
- HHVM is no longer supported
|
||||||
|
- Bugfix [#165](https://github.com/cocur/slugify/issues/165) Added missing French rules to `DefaultRuleProvider` (by [gsouf](https://github.com/gsouf))
|
||||||
|
- [#168](https://github.com/cocur/slugify/pull/168) Add Persian rules (by [mohammad6006](https://github.com/mohammad6006))
|
||||||
|
- Bugfix [#169](https://github.com/cocur/slugify/issues/169) Add missing `getName()` to `Cocur\Slugify\Bridge\Twig\SlugifyExtension` (by [TomCan](https://github.com/TomCan))
|
||||||
|
- [#172](https://github.com/cocur/slugify/pull/172) Sort rules in `DefaultRuleProvider` alphabetically (by [tbmatuka](https://github.com/tbmatuka))
|
||||||
|
- [#174](https://github.com/cocur/slugify/pull/174) Add Hungarian rules (by [rviktor87](https://github.com/rviktor87))
|
||||||
|
- [#180](https://github.com/cocur/slugify/pull/180) Add Brazilian Portuguese rules (by [tallesairan](https://github.com/tallesairan))
|
||||||
|
- Bugfix [#181](https://github.com/cocur/slugify/pull/181) Add missing French rules (by [FabienPapet](https://github.com/FabienPapet))
|
||||||
|
|
||||||
|
### Version 2.5 (23 March 2017)
|
||||||
|
|
||||||
|
- [#150](https://github.com/cocur/slugify/pull/150) Add Romanian rules (by [gabiudrescu](https://github.com/gabiudrescu))
|
||||||
|
- [#154](https://github.com/cocur/slugify/pull/154) Add French rules (by [SuN-80](https://github.com/SuN-80))
|
||||||
|
- [#159](https://github.com/cocur/slugify/pull/159) Add Estonian rules (by [erkimiilberg](https://github.com/erkimiilberg))
|
||||||
|
- [#162](https://github.com/cocur/slugify/pull/162) Add support for Twig 2 (by [JakeFr](https://github.com/JakeFr))
|
||||||
|
|
||||||
|
### Version 2.4 (9 February 2017)
|
||||||
|
|
||||||
|
- [#133](https://github.com/cocur/slugify/pull/133) Allow to modify options without creating a new object (by [leofeyer](https://github.com/leofeyer))
|
||||||
|
- [#135](https://github.com/cocur/slugify/pull/135) Add support for Danish (by [izehose](https://github.com/izehose))
|
||||||
|
- [#140](https://github.com/cocur/slugify/pull/140) Update Hindi support (by [arunlodhi](https://github.com/arunlodhi))
|
||||||
|
- [#146](https://github.com/cocur/slugify/pull/146) Add support for Italien (by [gianiaz](https://github.com/gianiaz))
|
||||||
|
- [#151](https://github.com/cocur/slugify/pull/151) Add support for Serbian (by [cvetan](https://github.com/cvetan))
|
||||||
|
- [#155](https://github.com/cocur/slugify/pull/155) Update support for Lithuanian (by [s4uliu5](https://github.com/s4uliu5))
|
||||||
|
|
||||||
|
### Version 2.3 (9 August 2016)
|
||||||
|
|
||||||
|
- [#124](https://github.com/cocur/slugify/issues/124) Fix support for Bulgarian
|
||||||
|
- [#125](https://github.com/cocur/slugify/pull/125) Update Silex 2 provider (by [JakeFr](https://github.com/JakeFr))
|
||||||
|
- [#129](https://github.com/cocur/slugify/pull/129) Add support for Croatian (by [napravicukod](https://github.com/napravicukod))
|
||||||
|
|
||||||
|
### Version 2.2 (10 July 2016)
|
||||||
|
|
||||||
|
- [#102](https://github.com/cocur/slugify/pull/102) Add transliterations for Azerbaijani (by [seferov](https://github.com/seferov))
|
||||||
|
- [#109](https://github.com/cocur/slugify/pull/109) Made integer values into strings (by [JonathanMH](https://github.com/JonathanMH))
|
||||||
|
- [#114](https://github.com/cocur/slugify/pull/114) Provide SlugifyServiceProvider for league/container (by [localheinz](https://github.com/localheinz))
|
||||||
|
- [#120](https://github.com/cocur/slugify/issues/120) Add compatibility with Silex 2 (by [shamotj](https://github.com/shamotj))
|
||||||
|
|
||||||
|
### Version 2.1.1 (8 April 2016)
|
||||||
|
|
||||||
|
- Do not activate Swedish rules by default (fixes broken v2.1 release)
|
||||||
|
|
||||||
|
### Version 2.1.0 (8 April 2016)
|
||||||
|
|
||||||
|
- [#104](https://github.com/cocur/slugify/pull/104) Add Symfony configuration (by [estahn](https://github.com/estahn))
|
||||||
|
- [#107](https://github.com/cocur/slugify/issues/107) Fix Swedish rules
|
||||||
|
|
||||||
|
### Version 2.0.0 (24 February 2016)
|
||||||
|
|
||||||
|
- [#78](https://github.com/cocur/slugify/pull/78) Use multibyte-safe case convention (by [Koc](https://github.com/Koc))
|
||||||
|
- [#81](https://github.com/cocur/slugify/pull/81) Move rules into JSON files (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
- [#84](https://github.com/cocur/slugify/pull/84) Add tests for very long strings containing umlauts (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
- [#88](https://github.com/cocur/slugify/pull/88) Add rules for Hindi (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
- [#89](https://github.com/cocur/slugify/pull/89) Add rules for Norwegian (by [tsmes](https://github.com/tsmes))
|
||||||
|
- [#90](https://github.com/cocur/slugify/pull/90) Replace `bindShared` with `singleton` in Laravel bridge (by [sunspikes](https://github.com/sunspikes))
|
||||||
|
- [#97](https://github.com/cocur/slugify/pull/97) Set minimum PHP version to 5.5.9 (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
- [#98](https://github.com/cocur/slugify/pull/98) Add rules for Bulgarian (by [RoumenDamianoff](https://github.com/RoumenDamianoff))
|
||||||
|
|
||||||
|
### Version 1.4.1 (11 February 2016)
|
||||||
|
|
||||||
|
- [#90](https://github.com/cocur/slugify/pull/90) Replace `bindShared` with `singleton` in Laravel bridge (by [sunspikes](https://github.com/sunspikes))
|
||||||
|
|
||||||
|
### Version 1.4 (29 September 2015)
|
||||||
|
|
||||||
|
- [#75](https://github.com/cocur/slugify/pull/75) Remove a duplicate array entry (by [irfanevrens](https://github.com/irfanevrens))
|
||||||
|
- [#76](https://github.com/cocur/slugify/pull/76) Add support for Georgian (by [TheGIBSON](https://github.com/TheGIBSON))
|
||||||
|
- [#77](https://github.com/cocur/slugify/pull/77) Fix Danish transliterations (by [kafoso](https://github.com/kafoso))
|
||||||
|
|
||||||
|
### Version 1.3 (2 September 2015)
|
||||||
|
|
||||||
|
- [#70](https://github.com/cocur/slugify/pull/70) Add missing superscript and subscript digits (by [BlueM](https://github.com/BlueM))
|
||||||
|
- [#71](https://github.com/cocur/slugify/pull/71) Improve Greek language support (by [kostaspt](https://github.com/kostaspt))
|
||||||
|
- [#72](https://github.com/cocur/slugify/pull/72) Improve Silex integration (by [CarsonF](https://github.com/CarsonF))
|
||||||
|
- [#73](https://github.com/cocur/slugify/pull/73) Improve Russian language support (by [akost](https://github.com/akost))
|
||||||
|
|
||||||
|
### Version 1.2 (2 July 2015)
|
||||||
|
|
||||||
|
- Add integration for [Plum](https://github.com/plumphp/plum) (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
- [#64](https://github.com/cocur/slugify/pull/64) Fix Nette integration (by [lookyman](https://github.com/lookyman))
|
||||||
|
- Add option to not convert slug to lowercase (by [florianeckerstorfer](https://github.com/florianeckerstorfer) and [GDmac](https://github.com/GDmac))
|
||||||
|
|
||||||
|
### Version 1.1 (18 March 2015)
|
||||||
|
|
||||||
|
- [#54](https://github.com/cocur/slugify/pull/54) Add support for Burmese characters (by [lovetostrike](https://github.com/lovetostrike))
|
||||||
|
- [#58](https://github.com/cocur/slugify/pull/58) Add Nette and Latte integration (by [lookyman](https://github.com/lookyman))
|
||||||
|
- [#50](https://github.com/cocur/slugify/issues/50) Fix transliteration for Vietnamese character Đ (by [mac2000](https://github.com/mac2000))
|
||||||
|
|
||||||
|
### Version 1.0 (26 November 2014)
|
||||||
|
|
||||||
|
No new features or bugfixes, but it's about time to pump Slugify to v1.0.
|
||||||
|
|
||||||
|
### Version 0.11 (23 November 2014)
|
||||||
|
|
||||||
|
- [#49](https://github.com/cocur/slugify/pull/49) Add Zend Framework 2 integration (by [acelaya](https://github.com/acelaya))
|
||||||
|
|
||||||
|
### Version 0.10.3 (8 November 2014)
|
||||||
|
|
||||||
|
- [#48](https://github.com/cocur/slugify/pull/48) Add support for Vietnamese (by [mac2000](https://github.com/mac2000))
|
||||||
|
|
||||||
|
### Version 0.10.2 (18 October 2014)
|
||||||
|
|
||||||
|
- [#44](https://github.com/cocur/slugify/pull/44) Change visibility of properties to `protected` (by [acelaya](https://github.com/acelaya))
|
||||||
|
- [#45](https://github.com/cocur/slugify/pull/45) Configure regular expression used to replace characters (by [acelaya](https://github.com/acelaya))
|
||||||
|
- Fix type hinting (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
- Remove duplicate rule (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
|
||||||
|
### Version 0.10.1 (1 September 2014)
|
||||||
|
|
||||||
|
- [#39](https://github.com/cocur/slugify/pull/39) Add support for rulesets (by [florianeckerstorfer](https://github.com/florianeckerstorfer))
|
||||||
|
|
||||||
|
### Version 0.10.0 (26 August 2014)
|
||||||
|
|
||||||
|
- [#32](https://github.com/cocur/slugify/pull/32) Added Laraval bridge (by [cviebrock](https://github.com/cviebrock))
|
||||||
|
- [#35](https://github.com/cocur/slugify/pull/35) Fixed transliteration for `Ď` (by [michalskop](https://github.com/michalskop))
|
||||||
|
|
||||||
|
### Version 0.9 (29 May 2014)
|
||||||
|
|
||||||
|
- [#28](https://github.com/cocur/slugify/pull/28) Add Symfony2 service alias and make Twig extension private (by [Kevin Bond](https://github.com/kbond))
|
||||||
|
|
||||||
|
### Version 0.8 (18 April 2014)
|
||||||
|
|
||||||
|
- [#27](https://github.com/cocur/slugify/pull/27) Add support for Arabic characters (by [Davide Bellini](https://github.com/billmn))
|
||||||
|
- Added some missing characters
|
||||||
|
- Improved organisation of characters in `Slugify` class
|
||||||
|
|
||||||
|
### Version 0.7 (4 April 2014)
|
||||||
|
|
||||||
|
This version introduces optional integrations into Symfony2, Silex and Twig. You can still use the library in any other framework. I decided to include these bridges because there exist integrations from other developers, but they use outdated versions of cocur/slugify. Including these small bridge classes in the library makes maintaining them a lot easier for me.
|
||||||
|
|
||||||
|
- [#23](https://github.com/cocur/slugify/pull/23) Added Symfony2 service
|
||||||
|
- [#24](https://github.com/cocur/slugify/pull/24) Added Twig extension
|
||||||
|
- [#25](https://github.com/cocur/slugify/pull/25) Added Silex service provider
|
||||||
|
|
||||||
|
### Version 0.6 (2 April 2014)
|
||||||
|
|
||||||
|
- [#22](https://github.com/cocur/slugify/pull/22) Added support for Esperanto characters (by [Michel Petit](https://github.com/malenkiki))
|
||||||
|
|
||||||
|
### Version 0.5 (28 March 2014)
|
||||||
|
|
||||||
|
- [#21](https://github.com/cocur/slugify/pull/21) Added support for Greek characters (by [Michel Petit](https://github.com/malenkiki))
|
||||||
|
- [#20](https://github.com/cocur/slugify/pull/20) Fixed rule for cyrillic letter D (by [Marchenko Alexandr](https://github.com/cocur/slugify/pull/20))
|
||||||
|
- Add missing `$separator` parameter to `SlugifyInterface`
|
||||||
|
|
||||||
|
### Version 0.4.1 (9 March 2014)
|
||||||
|
|
||||||
|
- [#19](https://github.com/cocur/slugify/pull/19) Adds soft sign rule (by [Marchenko Alexandr](https://github.com/mac2000))
|
||||||
|
|
||||||
|
### Version 0.4 (17 January 2014)
|
||||||
|
|
||||||
|
Nearly completely rewritten code, removes `iconv` support because the underlying library is broken. The code is now better and faster. Many thanks to [Marchenko Alexandr](http://mac-blog.org.ua).
|
||||||
|
|
||||||
|
### Version 0.3 (12 January 2014)
|
||||||
|
|
||||||
|
- [#11](https://github.com/cocur/slugify/pull/11) PSR-4 compatible (by [mac2000](https://github.com/mac2000))
|
||||||
|
- [#13](https://github.com/cocur/slugify/pull/13) Added editorconfig (by [mac2000](https://github.com/mac2000))
|
||||||
|
- [#14](https://github.com/cocur/slugify/pull/14) Return empty slug when input is empty and removed unused parameter (by [mac2000](https://github.com/mac2000))
|
||||||
|
|
||||||
|
## Authors
|
||||||
|
|
||||||
|
- [Florian Eckerstorfer](http://florian.ec) ([Twitter](http://twitter.com/Florian_))
|
||||||
|
- [Ivo Bathke](https://github.com/ivoba)
|
||||||
|
- [Marchenko Alexandr](http://mac-blog.org.ua)
|
||||||
|
- And many [great contributors](https://github.com/cocur/slugify/graphs/contributors)
|
||||||
|
|
||||||
|
Support for Chinese is adapted from [jifei/Pinyin](https://github.com/jifei/Pinyin) with permission.
|
||||||
|
|
||||||
|
> Slugify is a project of [Cocur](http://cocur.co). You can contact us on Twitter:
|
||||||
|
> [**@cocurco**](https://twitter.com/cocurco)
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
If you need support you can ask on [Twitter](https://twitter.com/cocurco) (well, only if your question is short) or you
|
||||||
|
can join our chat on Gitter.
|
||||||
|
|
||||||
|
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cocur/slugify)
|
||||||
|
|
||||||
|
In case you want to support the development of Slugify you can help us with providing additional transliterations or
|
||||||
|
inform us if a transliteration is wrong. We would highly appreciate it if you can send us directly a Pull Request on
|
||||||
|
Github. If you have never contributed to a project on Github we are happy to help you. Just ask on Twitter or directly
|
||||||
|
join our Gitter.
|
||||||
|
|
||||||
|
You always can help me (Florian, the original developer and maintainer) out by
|
||||||
|
[sending me an Euro or two](https://paypal.me/florianec/2).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2017 Florian Eckerstorfer
|
||||||
|
|
||||||
|
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.
|
59
plugins/page-toc/vendor/cocur/slugify/composer.json
vendored
Normal file
59
plugins/page-toc/vendor/cocur/slugify/composer.json
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"name": "cocur/slugify",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Converts a string into a slug.",
|
||||||
|
"keywords": [
|
||||||
|
"slug",
|
||||||
|
"slugify"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Florian Eckerstorfer",
|
||||||
|
"email": "florian@eckerstorfer.co",
|
||||||
|
"homepage": "https://florian.ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ivo Bathke",
|
||||||
|
"email": "ivo.bathke@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.0",
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/config": "<3.4 || >=4,<4.3",
|
||||||
|
"symfony/dependency-injection": "<3.4 || >=4,<4.3",
|
||||||
|
"symfony/http-kernel": "<3.4 || >=4,<4.3",
|
||||||
|
"twig/twig": "<2.12.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/framework": "~5.1",
|
||||||
|
"latte/latte": "~2.2",
|
||||||
|
"league/container": "^2.2.0",
|
||||||
|
"mikey179/vfsstream": "~1.6.8",
|
||||||
|
"mockery/mockery": "^1.3",
|
||||||
|
"nette/di": "~2.4",
|
||||||
|
"phpunit/phpunit": "^5.7.27",
|
||||||
|
"pimple/pimple": "~1.1",
|
||||||
|
"plumphp/plum": "~0.1",
|
||||||
|
"symfony/config": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"symfony/dependency-injection": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"symfony/http-kernel": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"twig/twig": "^2.12.1 || ~3.0",
|
||||||
|
"zendframework/zend-modulemanager": "~2.2",
|
||||||
|
"zendframework/zend-servicemanager": "~2.2",
|
||||||
|
"zendframework/zend-view": "~2.2"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Cocur\\Slugify\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Cocur\\Slugify\\Tests\\": "tests"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Laravel/SlugifyFacade.php
vendored
Normal file
39
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Laravel/SlugifyFacade.php
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Laravel;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlugifyFacade
|
||||||
|
*
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @author Colin Viebrock
|
||||||
|
* @copyright 2012-2014 Florian Eckerstorfer
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyFacade extends Facade
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the registered name of the component.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
protected static function getFacadeAccessor()
|
||||||
|
{
|
||||||
|
return 'slugify';
|
||||||
|
}
|
||||||
|
}
|
57
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Laravel/SlugifyServiceProvider.php
vendored
Normal file
57
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Laravel/SlugifyServiceProvider.php
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Laravel;
|
||||||
|
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlugifyServiceProvider
|
||||||
|
*
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @author Colin Viebrock
|
||||||
|
* @copyright 2012-2014 Florian Eckerstorfer
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyServiceProvider extends LaravelServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Indicates if loading of the provider is deferred.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $defer = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the service provider.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->app->singleton('slugify', function () {
|
||||||
|
return new Slugify();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the services provided by the provider.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function provides()
|
||||||
|
{
|
||||||
|
return ['slugify'];
|
||||||
|
}
|
||||||
|
}
|
38
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Latte/SlugifyHelper.php
vendored
Normal file
38
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Latte/SlugifyHelper.php
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Latte;
|
||||||
|
|
||||||
|
use Cocur\Slugify\SlugifyInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlugifyHelper
|
||||||
|
*
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @author Lukáš Unger <looky.msc@gmail.com>
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyHelper
|
||||||
|
{
|
||||||
|
/** @var SlugifyInterface */
|
||||||
|
private $slugify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct(SlugifyInterface $slugify)
|
||||||
|
{
|
||||||
|
$this->slugify = $slugify;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $string
|
||||||
|
* @param string|null $separator
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function slugify($string, $separator = null)
|
||||||
|
{
|
||||||
|
return $this->slugify->slugify($string, $separator);
|
||||||
|
}
|
||||||
|
}
|
37
plugins/page-toc/vendor/cocur/slugify/src/Bridge/League/SlugifyServiceProvider.php
vendored
Normal file
37
plugins/page-toc/vendor/cocur/slugify/src/Bridge/League/SlugifyServiceProvider.php
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\League;
|
||||||
|
|
||||||
|
use Cocur\Slugify\RuleProvider\DefaultRuleProvider;
|
||||||
|
use Cocur\Slugify\RuleProvider\RuleProviderInterface;
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
use Cocur\Slugify\SlugifyInterface;
|
||||||
|
use League\Container\ServiceProvider\AbstractServiceProvider;
|
||||||
|
|
||||||
|
class SlugifyServiceProvider extends AbstractServiceProvider
|
||||||
|
{
|
||||||
|
protected $provides = [
|
||||||
|
SlugifyInterface::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->container->share(SlugifyInterface::class, function () {
|
||||||
|
$options = [];
|
||||||
|
if ($this->container->has('config.slugify.options')) {
|
||||||
|
$options = $this->container->get('config.slugify.options');
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider = null;
|
||||||
|
if ($this->container->has(RuleProviderInterface::class)) {
|
||||||
|
/* @var RuleProviderInterface $provider */
|
||||||
|
$provider = $this->container->get(RuleProviderInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Slugify(
|
||||||
|
$options,
|
||||||
|
$provider
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
49
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Nette/SlugifyExtension.php
vendored
Normal file
49
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Nette/SlugifyExtension.php
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Nette;
|
||||||
|
|
||||||
|
use Nette\DI\CompilerExtension;
|
||||||
|
use Nette\DI\ServiceDefinition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlugifyExtension
|
||||||
|
*
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @author Lukáš Unger <looky.msc@gmail.com>
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyExtension extends CompilerExtension
|
||||||
|
{
|
||||||
|
public function loadConfiguration()
|
||||||
|
{
|
||||||
|
$builder = $this->getContainerBuilder();
|
||||||
|
|
||||||
|
$builder->addDefinition($this->prefix('slugify'))
|
||||||
|
->setClass('Cocur\Slugify\SlugifyInterface')
|
||||||
|
->setFactory('Cocur\Slugify\Slugify');
|
||||||
|
|
||||||
|
$builder->addDefinition($this->prefix('helper'))
|
||||||
|
->setClass('Cocur\Slugify\Bridge\Latte\SlugifyHelper')
|
||||||
|
->setAutowired(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beforeCompile()
|
||||||
|
{
|
||||||
|
$builder = $this->getContainerBuilder();
|
||||||
|
|
||||||
|
$self = $this;
|
||||||
|
$registerToLatte = function (ServiceDefinition $def) use ($self) {
|
||||||
|
$def->addSetup('addFilter', ['slugify', [$self->prefix('@helper'), 'slugify']]);
|
||||||
|
};
|
||||||
|
|
||||||
|
$latteFactory = $builder->getByType('Nette\Bridges\ApplicationLatte\ILatteFactory') ?: 'nette.latteFactory';
|
||||||
|
if ($builder->hasDefinition($latteFactory)) {
|
||||||
|
$registerToLatte($builder->getDefinition($latteFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($builder->hasDefinition('nette.latte')) {
|
||||||
|
$registerToLatte($builder->getDefinition('nette.latte'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Plum/SlugifyConverter.php
vendored
Normal file
50
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Plum/SlugifyConverter.php
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Plum;
|
||||||
|
|
||||||
|
use Plum\Plum\Converter\ConverterInterface;
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
use Cocur\Slugify\SlugifyInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlugifyConverter
|
||||||
|
*
|
||||||
|
* @package Cocur\Slugify\Bridge\Plum
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @copyright 2015 Florian Eckerstorfer
|
||||||
|
*/
|
||||||
|
class SlugifyConverter implements ConverterInterface
|
||||||
|
{
|
||||||
|
/** @var Slugify */
|
||||||
|
private $slugify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SlugifyInterface|null $slugify
|
||||||
|
*/
|
||||||
|
public function __construct(SlugifyInterface $slugify = null)
|
||||||
|
{
|
||||||
|
if ($slugify === null) {
|
||||||
|
$slugify = new Slugify();
|
||||||
|
}
|
||||||
|
$this->slugify = $slugify;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $item
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function convert($item)
|
||||||
|
{
|
||||||
|
return $this->slugify->slugify($item);
|
||||||
|
}
|
||||||
|
}
|
31
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Symfony/CocurSlugifyBundle.php
vendored
Normal file
31
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Symfony/CocurSlugifyBundle.php
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Symfony;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CocurSlugifyBundle
|
||||||
|
*
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @copyright 2012-2014 Florian Eckerstorfer
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class CocurSlugifyBundle extends Bundle
|
||||||
|
{
|
||||||
|
public function getContainerExtension()
|
||||||
|
{
|
||||||
|
return new CocurSlugifyExtension();
|
||||||
|
}
|
||||||
|
}
|
62
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Symfony/CocurSlugifyExtension.php
vendored
Normal file
62
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Symfony/CocurSlugifyExtension.php
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Symfony;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CocurSlugifyExtension
|
||||||
|
*
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @copyright 2012-2014 Florian Eckerstorfer
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class CocurSlugifyExtension extends Extension
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @param mixed[] $configs
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*/
|
||||||
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$configuration = new Configuration();
|
||||||
|
$config = $this->processConfiguration($configuration, $configs);
|
||||||
|
|
||||||
|
if (empty($config['rulesets'])) {
|
||||||
|
unset($config['rulesets']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract slugify arguments from config
|
||||||
|
$slugifyArguments = array_intersect_key($config, array_flip(['lowercase', 'trim', 'strip_tags', 'separator', 'regexp', 'rulesets']));
|
||||||
|
|
||||||
|
$container->setDefinition('cocur_slugify', new Definition('Cocur\Slugify\Slugify', [$slugifyArguments]));
|
||||||
|
$container
|
||||||
|
->setDefinition(
|
||||||
|
'cocur_slugify.twig.slugify',
|
||||||
|
new Definition(
|
||||||
|
'Cocur\Slugify\Bridge\Twig\SlugifyExtension',
|
||||||
|
[new Reference('cocur_slugify')]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->addTag('twig.extension')
|
||||||
|
->setPublic(false);
|
||||||
|
$container->setAlias('slugify', 'cocur_slugify');
|
||||||
|
$container->setAlias('Cocur\Slugify\SlugifyInterface', 'cocur_slugify');
|
||||||
|
}
|
||||||
|
}
|
46
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Symfony/Configuration.php
vendored
Normal file
46
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Symfony/Configuration.php
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the cocur/slugify package.
|
||||||
|
*
|
||||||
|
* (c) Enrico Stahn <enrico.stahn@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Symfony;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||||
|
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||||
|
|
||||||
|
class Configuration implements ConfigurationInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getConfigTreeBuilder()
|
||||||
|
{
|
||||||
|
$treeBuilder = new TreeBuilder('cocur_slugify');
|
||||||
|
|
||||||
|
// Keep compatibility with symfony/config < 4.2
|
||||||
|
if (\method_exists($treeBuilder, 'getRootNode')) {
|
||||||
|
$rootNode = $treeBuilder->getRootNode();
|
||||||
|
} else {
|
||||||
|
$rootNode = $treeBuilder->root('cocur_slugify');
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootNode
|
||||||
|
->children()
|
||||||
|
->booleanNode('lowercase')->end()
|
||||||
|
->booleanNode('lowercase_after_regexp')->end()
|
||||||
|
->booleanNode('trim')->end()
|
||||||
|
->booleanNode('strip_tags')->end()
|
||||||
|
->scalarNode('separator')->end()
|
||||||
|
->scalarNode('regexp')->end()
|
||||||
|
->arrayNode('rulesets')->prototype('scalar')->end()
|
||||||
|
->end();
|
||||||
|
|
||||||
|
return $treeBuilder;
|
||||||
|
}
|
||||||
|
}
|
80
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Twig/SlugifyExtension.php
vendored
Normal file
80
plugins/page-toc/vendor/cocur/slugify/src/Bridge/Twig/SlugifyExtension.php
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\Twig;
|
||||||
|
|
||||||
|
use Cocur\Slugify\SlugifyInterface;
|
||||||
|
use Twig\Extension\AbstractExtension;
|
||||||
|
use Twig\TwigFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlugifyExtension
|
||||||
|
*
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @copyright 2012-2015 Florian Eckerstorfer
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyExtension extends AbstractExtension
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SlugifyInterface
|
||||||
|
*/
|
||||||
|
private $slugify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param SlugifyInterface $slugify
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct(SlugifyInterface $slugify)
|
||||||
|
{
|
||||||
|
$this->slugify = $slugify;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Twig functions of this extension.
|
||||||
|
*
|
||||||
|
* @return TwigFilter[]
|
||||||
|
*/
|
||||||
|
public function getFilters()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFilter('slugify', [$this, 'slugifyFilter']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slugify filter.
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @param string|null $separator
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function slugifyFilter($string, $separator = null)
|
||||||
|
{
|
||||||
|
return $this->slugify->slugify($string, $separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return "SlugifyExtension";
|
||||||
|
}
|
||||||
|
}
|
50
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/Module.php
vendored
Normal file
50
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/Module.php
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\ZF2;
|
||||||
|
|
||||||
|
use Zend\ModuleManager\Feature\ServiceProviderInterface;
|
||||||
|
use Zend\ModuleManager\Feature\ViewHelperProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Module
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class Module implements ServiceProviderInterface, ViewHelperProviderInterface
|
||||||
|
{
|
||||||
|
const CONFIG_KEY = 'cocur_slugify';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected to return \Zend\ServiceManager\Config object or array to
|
||||||
|
* seed such an object.
|
||||||
|
*
|
||||||
|
* @return array<string,array<string,string>>
|
||||||
|
*/
|
||||||
|
public function getServiceConfig()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'factories' => [
|
||||||
|
'Cocur\Slugify\Slugify' => 'Cocur\Slugify\Bridge\ZF2\SlugifyService'
|
||||||
|
],
|
||||||
|
'aliases' => [
|
||||||
|
'slugify' => 'Cocur\Slugify\Slugify'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected to return \Zend\ServiceManager\Config object or array to
|
||||||
|
* seed such an object.
|
||||||
|
*
|
||||||
|
* @return array<string,array<string,string>>|\Zend\ServiceManager\Config
|
||||||
|
*/
|
||||||
|
public function getViewHelperConfig()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'factories' => [
|
||||||
|
'slugify' => 'Cocur\Slugify\Bridge\ZF2\SlugifyViewHelperFactory'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
30
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/SlugifyService.php
vendored
Normal file
30
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/SlugifyService.php
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\ZF2;
|
||||||
|
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SlugifyService
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param ServiceManager $sm
|
||||||
|
*
|
||||||
|
* @return Slugify
|
||||||
|
*/
|
||||||
|
public function __invoke($sm)
|
||||||
|
{
|
||||||
|
$config = $sm->get('Config');
|
||||||
|
|
||||||
|
$options = isset($config[Module::CONFIG_KEY]['options']) ? $config[Module::CONFIG_KEY]['options'] : [];
|
||||||
|
$provider = isset($config[Module::CONFIG_KEY]['provider']) ? $config[Module::CONFIG_KEY]['provider'] : null;
|
||||||
|
|
||||||
|
return new Slugify($options, $provider);
|
||||||
|
}
|
||||||
|
}
|
41
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/SlugifyViewHelper.php
vendored
Normal file
41
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/SlugifyViewHelper.php
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\ZF2;
|
||||||
|
|
||||||
|
use Cocur\Slugify\SlugifyInterface;
|
||||||
|
use Zend\View\Helper\AbstractHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SlugifyViewHelper
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyViewHelper extends AbstractHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SlugifyInterface
|
||||||
|
*/
|
||||||
|
protected $slugify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SlugifyInterface $slugify
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct(SlugifyInterface $slugify)
|
||||||
|
{
|
||||||
|
$this->slugify = $slugify;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $string
|
||||||
|
* @param string|null $separator
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __invoke($string, $separator = null)
|
||||||
|
{
|
||||||
|
return $this->slugify->slugify($string, $separator);
|
||||||
|
}
|
||||||
|
}
|
28
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/SlugifyViewHelperFactory.php
vendored
Normal file
28
plugins/page-toc/vendor/cocur/slugify/src/Bridge/ZF2/SlugifyViewHelperFactory.php
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\Bridge\ZF2;
|
||||||
|
|
||||||
|
use Cocur\Slugify\Slugify;
|
||||||
|
use Zend\View\HelperPluginManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SlugifyViewHelperFactory
|
||||||
|
* @package cocur/slugify
|
||||||
|
* @subpackage bridge
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class SlugifyViewHelperFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param HelperPluginManager $vhm
|
||||||
|
*
|
||||||
|
* @return SlugifyViewHelper
|
||||||
|
*/
|
||||||
|
public function __invoke($vhm)
|
||||||
|
{
|
||||||
|
/** @var Slugify $slugify */
|
||||||
|
$slugify = $vhm->getServiceLocator()->get('Cocur\Slugify\Slugify');
|
||||||
|
|
||||||
|
return new SlugifyViewHelper($slugify);
|
||||||
|
}
|
||||||
|
}
|
8557
plugins/page-toc/vendor/cocur/slugify/src/RuleProvider/DefaultRuleProvider.php
vendored
Normal file
8557
plugins/page-toc/vendor/cocur/slugify/src/RuleProvider/DefaultRuleProvider.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
47
plugins/page-toc/vendor/cocur/slugify/src/RuleProvider/FileRuleProvider.php
vendored
Normal file
47
plugins/page-toc/vendor/cocur/slugify/src/RuleProvider/FileRuleProvider.php
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\RuleProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FileRuleProvider
|
||||||
|
*
|
||||||
|
* @package Cocur\Slugify\RuleProvider
|
||||||
|
* @author Florian Eckerstorfer
|
||||||
|
* @copyright 2015 Florian Eckerstorfer
|
||||||
|
*/
|
||||||
|
class FileRuleProvider implements RuleProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $directoryName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $directoryName
|
||||||
|
*/
|
||||||
|
public function __construct($directoryName)
|
||||||
|
{
|
||||||
|
$this->directoryName = $directoryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $ruleset
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getRules($ruleset)
|
||||||
|
{
|
||||||
|
$fileName = $this->directoryName.DIRECTORY_SEPARATOR.$ruleset.'.json';
|
||||||
|
|
||||||
|
return json_decode(file_get_contents($fileName), true);
|
||||||
|
}
|
||||||
|
}
|
29
plugins/page-toc/vendor/cocur/slugify/src/RuleProvider/RuleProviderInterface.php
vendored
Normal file
29
plugins/page-toc/vendor/cocur/slugify/src/RuleProvider/RuleProviderInterface.php
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify\RuleProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RuleProviderInterface
|
||||||
|
*
|
||||||
|
* @package Cocur\Slugify\RuleProvider
|
||||||
|
* @author Florian Eckerstorfer
|
||||||
|
* @copyright 2015 Florian Eckerstorfer
|
||||||
|
*/
|
||||||
|
interface RuleProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param $ruleset
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getRules($ruleset);
|
||||||
|
}
|
192
plugins/page-toc/vendor/cocur/slugify/src/Slugify.php
vendored
Normal file
192
plugins/page-toc/vendor/cocur/slugify/src/Slugify.php
vendored
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify;
|
||||||
|
|
||||||
|
use Cocur\Slugify\RuleProvider\DefaultRuleProvider;
|
||||||
|
use Cocur\Slugify\RuleProvider\RuleProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slugify
|
||||||
|
*
|
||||||
|
* @package Cocur\Slugify
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @author Ivo Bathke <ivo.bathke@gmail.com>
|
||||||
|
* @author Marchenko Alexandr
|
||||||
|
* @copyright 2012-2015 Florian Eckerstorfer
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
class Slugify implements SlugifyInterface
|
||||||
|
{
|
||||||
|
const LOWERCASE_NUMBERS_DASHES = '/[^A-Za-z0-9]+/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string,string>
|
||||||
|
*/
|
||||||
|
protected $rules = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RuleProviderInterface
|
||||||
|
*/
|
||||||
|
protected $provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string,mixed>
|
||||||
|
*/
|
||||||
|
protected $options = [
|
||||||
|
'regexp' => self::LOWERCASE_NUMBERS_DASHES,
|
||||||
|
'separator' => '-',
|
||||||
|
'lowercase' => true,
|
||||||
|
'lowercase_after_regexp' => false,
|
||||||
|
'trim' => true,
|
||||||
|
'strip_tags' => false,
|
||||||
|
'rulesets' => [
|
||||||
|
'default',
|
||||||
|
// Languages are preferred if they appear later, list is ordered by number of
|
||||||
|
// websites in that language
|
||||||
|
// https://en.wikipedia.org/wiki/Languages_used_on_the_Internet#Content_languages_for_websites
|
||||||
|
'armenian',
|
||||||
|
'azerbaijani',
|
||||||
|
'burmese',
|
||||||
|
'hindi',
|
||||||
|
'georgian',
|
||||||
|
'norwegian',
|
||||||
|
'vietnamese',
|
||||||
|
'ukrainian',
|
||||||
|
'latvian',
|
||||||
|
'finnish',
|
||||||
|
'greek',
|
||||||
|
'czech',
|
||||||
|
'arabic',
|
||||||
|
'slovak',
|
||||||
|
'turkish',
|
||||||
|
'polish',
|
||||||
|
'german',
|
||||||
|
'russian',
|
||||||
|
'romanian'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $options
|
||||||
|
* @param RuleProviderInterface $provider
|
||||||
|
*/
|
||||||
|
public function __construct(array $options = [], RuleProviderInterface $provider = null)
|
||||||
|
{
|
||||||
|
$this->options = array_merge($this->options, $options);
|
||||||
|
$this->provider = $provider ? $provider : new DefaultRuleProvider();
|
||||||
|
|
||||||
|
foreach ($this->options['rulesets'] as $ruleSet) {
|
||||||
|
$this->activateRuleSet($ruleSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slug-version of the string.
|
||||||
|
*
|
||||||
|
* @param string $string String to slugify
|
||||||
|
* @param string|array|null $options Options
|
||||||
|
*
|
||||||
|
* @return string Slugified version of the string
|
||||||
|
*/
|
||||||
|
public function slugify($string, $options = null)
|
||||||
|
{
|
||||||
|
// BC: the second argument used to be the separator
|
||||||
|
if (is_string($options)) {
|
||||||
|
$separator = $options;
|
||||||
|
$options = [];
|
||||||
|
$options['separator'] = $separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = array_merge($this->options, (array) $options);
|
||||||
|
|
||||||
|
// Add a custom ruleset without touching the default rules
|
||||||
|
if (isset($options['ruleset'])) {
|
||||||
|
$rules = array_merge($this->rules, $this->provider->getRules($options['ruleset']));
|
||||||
|
} else {
|
||||||
|
$rules = $this->rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
$string = ($options['strip_tags'])
|
||||||
|
? strip_tags($string)
|
||||||
|
: $string;
|
||||||
|
|
||||||
|
$string = strtr($string, $rules);
|
||||||
|
unset($rules);
|
||||||
|
|
||||||
|
if ($options['lowercase'] && !$options['lowercase_after_regexp']) {
|
||||||
|
$string = mb_strtolower($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
$string = preg_replace($options['regexp'], $options['separator'], $string);
|
||||||
|
|
||||||
|
if ($options['lowercase'] && $options['lowercase_after_regexp']) {
|
||||||
|
$string = mb_strtolower($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($options['trim'])
|
||||||
|
? trim($string, $options['separator'])
|
||||||
|
: $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a custom rule to Slugify.
|
||||||
|
*
|
||||||
|
* @param string $character Character
|
||||||
|
* @param string $replacement Replacement character
|
||||||
|
*
|
||||||
|
* @return Slugify
|
||||||
|
*/
|
||||||
|
public function addRule($character, $replacement)
|
||||||
|
{
|
||||||
|
$this->rules[$character] = $replacement;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds multiple rules to Slugify.
|
||||||
|
*
|
||||||
|
* @param array <string,string> $rules
|
||||||
|
*
|
||||||
|
* @return Slugify
|
||||||
|
*/
|
||||||
|
public function addRules(array $rules)
|
||||||
|
{
|
||||||
|
foreach ($rules as $character => $replacement) {
|
||||||
|
$this->addRule($character, $replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $ruleSet
|
||||||
|
*
|
||||||
|
* @return Slugify
|
||||||
|
*/
|
||||||
|
public function activateRuleSet($ruleSet)
|
||||||
|
{
|
||||||
|
return $this->addRules($this->provider->getRules($ruleSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static method to create new instance of {@see Slugify}.
|
||||||
|
*
|
||||||
|
* @param array <string,mixed> $options
|
||||||
|
*
|
||||||
|
* @return Slugify
|
||||||
|
*/
|
||||||
|
public static function create(array $options = [])
|
||||||
|
{
|
||||||
|
return new static($options);
|
||||||
|
}
|
||||||
|
}
|
36
plugins/page-toc/vendor/cocur/slugify/src/SlugifyInterface.php
vendored
Normal file
36
plugins/page-toc/vendor/cocur/slugify/src/SlugifyInterface.php
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of cocur/slugify.
|
||||||
|
*
|
||||||
|
* (c) Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cocur\Slugify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SlugifyInterface
|
||||||
|
*
|
||||||
|
* @package org.cocur.slugify
|
||||||
|
* @author Florian Eckerstorfer <florian@eckerstorfer.co>
|
||||||
|
* @author Marchenko Alexandr
|
||||||
|
* @copyright 2012-2014 Florian Eckerstorfer
|
||||||
|
* @license http://www.opensource.org/licenses/MIT The MIT License
|
||||||
|
*/
|
||||||
|
interface SlugifyInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return a URL safe version of a string.
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @param string|array|null $options
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
public function slugify($string, $options = null);
|
||||||
|
}
|
572
plugins/page-toc/vendor/composer/ClassLoader.php
vendored
Normal file
572
plugins/page-toc/vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,572 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var ?string */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array<int, string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array<string, string[]>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
* @psalm-var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool[]
|
||||||
|
* @psalm-var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var ?string */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var self[]
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ?string $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array<string, array<int, string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array<string, string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array<string, string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[] Array of classname => path
|
||||||
|
* @psalm-var array<string, string>
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $classMap Class to filename map
|
||||||
|
* @psalm-param array<string, string> $classMap
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param string[]|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param string[]|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param string[]|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param string[]|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return self[]
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function includeFile($file)
|
||||||
|
{
|
||||||
|
include $file;
|
||||||
|
}
|
350
plugins/page-toc/vendor/composer/InstalledVersions.php
vendored
Normal file
350
plugins/page-toc/vendor/composer/InstalledVersions.php
vendored
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints($constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
||||||
|
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||||
|
self::$installed = $installed[count($installed) - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = require __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
21
plugins/page-toc/vendor/composer/LICENSE
vendored
Normal file
21
plugins/page-toc/vendor/composer/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
11
plugins/page-toc/vendor/composer/autoload_classmap.php
vendored
Normal file
11
plugins/page-toc/vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
'Grav\\Plugin\\PageTOCPlugin' => $baseDir . '/page-toc.php',
|
||||||
|
);
|
9
plugins/page-toc/vendor/composer/autoload_namespaces.php
vendored
Normal file
9
plugins/page-toc/vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
12
plugins/page-toc/vendor/composer/autoload_psr4.php
vendored
Normal file
12
plugins/page-toc/vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Knp\\Menu\\' => array($vendorDir . '/knplabs/knp-menu/src/Knp/Menu'),
|
||||||
|
'Grav\\Plugin\\PageToc\\' => array($baseDir . '/classes'),
|
||||||
|
'Cocur\\Slugify\\' => array($vendorDir . '/cocur/slugify/src'),
|
||||||
|
);
|
57
plugins/page-toc/vendor/composer/autoload_real.php
vendored
Normal file
57
plugins/page-toc/vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInit88b09b7fda3e99f2e22346b58205e375
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit88b09b7fda3e99f2e22346b58205e375', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit88b09b7fda3e99f2e22346b58205e375', 'loadClassLoader'));
|
||||||
|
|
||||||
|
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||||
|
if ($useStaticLoader) {
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit88b09b7fda3e99f2e22346b58205e375::getInitializer($loader));
|
||||||
|
} else {
|
||||||
|
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->set($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = require __DIR__ . '/autoload_psr4.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->setPsr4($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||||
|
if ($classMap) {
|
||||||
|
$loader->addClassMap($classMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
53
plugins/page-toc/vendor/composer/autoload_static.php
vendored
Normal file
53
plugins/page-toc/vendor/composer/autoload_static.php
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInit88b09b7fda3e99f2e22346b58205e375
|
||||||
|
{
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'K' =>
|
||||||
|
array (
|
||||||
|
'Knp\\Menu\\' => 9,
|
||||||
|
),
|
||||||
|
'G' =>
|
||||||
|
array (
|
||||||
|
'Grav\\Plugin\\PageToc\\' => 20,
|
||||||
|
),
|
||||||
|
'C' =>
|
||||||
|
array (
|
||||||
|
'Cocur\\Slugify\\' => 14,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'Knp\\Menu\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/knplabs/knp-menu/src/Knp/Menu',
|
||||||
|
),
|
||||||
|
'Grav\\Plugin\\PageToc\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/../..' . '/classes',
|
||||||
|
),
|
||||||
|
'Cocur\\Slugify\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/cocur/slugify/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
|
'Grav\\Plugin\\PageTOCPlugin' => __DIR__ . '/../..' . '/page-toc.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInit88b09b7fda3e99f2e22346b58205e375::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInit88b09b7fda3e99f2e22346b58205e375::$prefixDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInit88b09b7fda3e99f2e22346b58205e375::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
158
plugins/page-toc/vendor/composer/installed.json
vendored
Normal file
158
plugins/page-toc/vendor/composer/installed.json
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "cocur/slugify",
|
||||||
|
"version": "v4.0.0",
|
||||||
|
"version_normalized": "4.0.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/cocur/slugify.git",
|
||||||
|
"reference": "3f1ffc300f164f23abe8b64ffb3f92d35cec8307"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/cocur/slugify/zipball/3f1ffc300f164f23abe8b64ffb3f92d35cec8307",
|
||||||
|
"reference": "3f1ffc300f164f23abe8b64ffb3f92d35cec8307",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"php": ">=7.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/config": "<3.4 || >=4,<4.3",
|
||||||
|
"symfony/dependency-injection": "<3.4 || >=4,<4.3",
|
||||||
|
"symfony/http-kernel": "<3.4 || >=4,<4.3",
|
||||||
|
"twig/twig": "<2.12.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/framework": "~5.1",
|
||||||
|
"latte/latte": "~2.2",
|
||||||
|
"league/container": "^2.2.0",
|
||||||
|
"mikey179/vfsstream": "~1.6.8",
|
||||||
|
"mockery/mockery": "^1.3",
|
||||||
|
"nette/di": "~2.4",
|
||||||
|
"phpunit/phpunit": "^5.7.27",
|
||||||
|
"pimple/pimple": "~1.1",
|
||||||
|
"plumphp/plum": "~0.1",
|
||||||
|
"symfony/config": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"symfony/dependency-injection": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"symfony/http-kernel": "^3.4 || ^4.3 || ^5.0",
|
||||||
|
"twig/twig": "^2.12.1 || ~3.0",
|
||||||
|
"zendframework/zend-modulemanager": "~2.2",
|
||||||
|
"zendframework/zend-servicemanager": "~2.2",
|
||||||
|
"zendframework/zend-view": "~2.2"
|
||||||
|
},
|
||||||
|
"time": "2019-12-14T13:04:14+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Cocur\\Slugify\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Florian Eckerstorfer",
|
||||||
|
"email": "florian@eckerstorfer.co",
|
||||||
|
"homepage": "https://florian.ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ivo Bathke",
|
||||||
|
"email": "ivo.bathke@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Converts a string into a slug.",
|
||||||
|
"keywords": [
|
||||||
|
"slug",
|
||||||
|
"slugify"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/cocur/slugify/issues",
|
||||||
|
"source": "https://github.com/cocur/slugify/tree/master"
|
||||||
|
},
|
||||||
|
"install-path": "../cocur/slugify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "knplabs/knp-menu",
|
||||||
|
"version": "v3.3.0",
|
||||||
|
"version_normalized": "3.3.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/KnpLabs/KnpMenu.git",
|
||||||
|
"reference": "8bd3dc2afa22c65617c563c5e25e62d6e23e98c7"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/KnpLabs/KnpMenu/zipball/8bd3dc2afa22c65617c563c5e25e62d6e23e98c7",
|
||||||
|
"reference": "8bd3dc2afa22c65617c563c5e25e62d6e23e98c7",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.3 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"twig/twig": "<1.40 || >=2,<2.9"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"psr/container": "^1.0",
|
||||||
|
"symfony/http-foundation": "^4.4 || ^5.0 || ^6.0",
|
||||||
|
"symfony/phpunit-bridge": "^5.3",
|
||||||
|
"symfony/routing": "^4.4 || ^5.0 || ^6.0",
|
||||||
|
"twig/twig": "^1.40 || ^2.9 || ^3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"twig/twig": "for the TwigRenderer and the integration with your templates"
|
||||||
|
},
|
||||||
|
"time": "2021-10-23T15:01:04+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.3-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "source",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Knp\\Menu\\": "src/Knp/Menu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "KnpLabs",
|
||||||
|
"homepage": "https://knplabs.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Christophe Coevoet",
|
||||||
|
"email": "stof@notk.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "The Community",
|
||||||
|
"homepage": "https://github.com/KnpLabs/KnpMenu/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An object oriented menu library",
|
||||||
|
"homepage": "https://knplabs.com",
|
||||||
|
"keywords": [
|
||||||
|
"menu",
|
||||||
|
"tree"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/KnpLabs/KnpMenu/issues",
|
||||||
|
"source": "https://github.com/KnpLabs/KnpMenu/tree/v3.3.0"
|
||||||
|
},
|
||||||
|
"install-path": "../knplabs/knp-menu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"dev-package-names": []
|
||||||
|
}
|
41
plugins/page-toc/vendor/composer/installed.php
vendored
Normal file
41
plugins/page-toc/vendor/composer/installed.php
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php return array(
|
||||||
|
'root' => array(
|
||||||
|
'pretty_version' => 'dev-develop',
|
||||||
|
'version' => 'dev-develop',
|
||||||
|
'type' => 'grav-plugin',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'reference' => '435d66f673904945450ba0f04add1a4b765abe7b',
|
||||||
|
'name' => 'trilbymedia/page-toc',
|
||||||
|
'dev' => true,
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'cocur/slugify' => array(
|
||||||
|
'pretty_version' => 'v4.0.0',
|
||||||
|
'version' => '4.0.0.0',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../cocur/slugify',
|
||||||
|
'aliases' => array(),
|
||||||
|
'reference' => '3f1ffc300f164f23abe8b64ffb3f92d35cec8307',
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'knplabs/knp-menu' => array(
|
||||||
|
'pretty_version' => 'v3.3.0',
|
||||||
|
'version' => '3.3.0.0',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../knplabs/knp-menu',
|
||||||
|
'aliases' => array(),
|
||||||
|
'reference' => '8bd3dc2afa22c65617c563c5e25e62d6e23e98c7',
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'trilbymedia/page-toc' => array(
|
||||||
|
'pretty_version' => 'dev-develop',
|
||||||
|
'version' => 'dev-develop',
|
||||||
|
'type' => 'grav-plugin',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'reference' => '435d66f673904945450ba0f04add1a4b765abe7b',
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
26
plugins/page-toc/vendor/composer/platform_check.php
vendored
Normal file
26
plugins/page-toc/vendor/composer/platform_check.php
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// platform_check.php @generated by Composer
|
||||||
|
|
||||||
|
$issues = array();
|
||||||
|
|
||||||
|
if (!(PHP_VERSION_ID >= 70306)) {
|
||||||
|
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.6". You are running ' . PHP_VERSION . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($issues) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trigger_error(
|
||||||
|
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
1
plugins/page-toc/vendor/composer/tmp-f711f3efca8baf836db7f0f0b18aef94~
vendored
Normal file
1
plugins/page-toc/vendor/composer/tmp-f711f3efca8baf836db7f0f0b18aef94~
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
|
174
plugins/page-toc/vendor/knplabs/knp-menu/CHANGELOG.md
vendored
Normal file
174
plugins/page-toc/vendor/knplabs/knp-menu/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
## 3.2 (2021-05-28)
|
||||||
|
|
||||||
|
* Remove Symfony 6 deprecations
|
||||||
|
* Enforce phpstan rules (max level)
|
||||||
|
|
||||||
|
## 3.1 (2019-12-01)
|
||||||
|
|
||||||
|
* Allowed Symfony 5 components
|
||||||
|
* Removed support for unsupported Symfony versions (4.0 and 4.1)
|
||||||
|
* Allowed Twig 3
|
||||||
|
|
||||||
|
## 3.0 (2019-09-02)
|
||||||
|
|
||||||
|
* Raised PHP requirements
|
||||||
|
* [BC break] Enforced strong types on all interfaces and classes
|
||||||
|
* [BC break] Removed deprecated features. Specifically, MenuFactory and MenuItem are not accepting a `null` name anymore
|
||||||
|
|
||||||
|
## 2.4 (2019-07-29)
|
||||||
|
|
||||||
|
* Fixed Twig deprecations
|
||||||
|
* Switched to namespaced Twig
|
||||||
|
* Fixed sprintf use
|
||||||
|
|
||||||
|
## 2.3 (2017-11-18)
|
||||||
|
|
||||||
|
* Deprecated the Silex 1 KnpMenuServiceProvider. Use the `knplabs/knp-menu-silex` package instead.
|
||||||
|
* Fixed RouteVoter to also match on non-string request arguments like integers as long as both string representations are identical.
|
||||||
|
* Add Symfony 4 support
|
||||||
|
|
||||||
|
## 2.2 (2016-09-22)
|
||||||
|
|
||||||
|
* Added a new function to twig: `knp_menu_get_current_item`
|
||||||
|
|
||||||
|
## 2.1.1 (2016-01-08)
|
||||||
|
|
||||||
|
* Made compatible with Symfony 3
|
||||||
|
|
||||||
|
## 2.1.0 (2015-09-20)
|
||||||
|
|
||||||
|
* Added a new function to twig: `knp_menu_get_breadcrumbs_array`
|
||||||
|
* Added a new filter to twig: `knp_menu_as_string`
|
||||||
|
* Added 2 new tests to twig: `knp_menu_current`, `knp_menu_ancestor`
|
||||||
|
* Made the templates compatible with Twig 2
|
||||||
|
* Add menu and renderer providers supporting any ArrayAccess implementations. The
|
||||||
|
Pimple-based providers (supporting only Pimple 1) are dperecated in favor of these
|
||||||
|
new providers.
|
||||||
|
|
||||||
|
## 2.0.1 (2014-08-01)
|
||||||
|
|
||||||
|
* Fixed voter conventions on RouteVoter
|
||||||
|
|
||||||
|
## 2.0.0 (2014-07-18)
|
||||||
|
|
||||||
|
* [BC break] Clean code and removed the BC layer
|
||||||
|
|
||||||
|
## 2.0.0 beta 1 (2014-06-19)
|
||||||
|
|
||||||
|
* [BC break] Added the new `Integration` namespace and removed the `Silex` one.
|
||||||
|
* Added a new Voter based on regular expression: `Knp\Menu\Matcher\Voter\RegexVoter`
|
||||||
|
|
||||||
|
## 2.0.0 alpha 2 (2014-05-01)
|
||||||
|
|
||||||
|
* [BC break] Changed the TwigRenderer to accept a menu template only as a string
|
||||||
|
* [BC break] Refactored the way of rendering twig templates. Every template should extends
|
||||||
|
the `knp_menu.html.twig` template.
|
||||||
|
* Introduced extension points in the MenuFactory through `Knp\Menu\Factory\ExtensionInterface`
|
||||||
|
* [BC break compared to 2.0 alpha 1] The inheritance extension points introduced in alpha1 are
|
||||||
|
deprecated in favor of extensions and will be removed before the stable release.
|
||||||
|
* `Knp\Menu\Silex\RouterAwareFactory` is deprecated in favor of `Knp\Menu\Silex\RoutingExtension`.
|
||||||
|
* [BC break] Deprecated the methods `createFromArray` and `createFromNode` in the MenuFactory and
|
||||||
|
removed them from `Knp\Menu\FactoryInterface`. Use `Knp\Menu\Loader\ArrayLoader` and
|
||||||
|
`Knp\Menu\Loader\NodeLoader` instead.
|
||||||
|
* [BC break] Deprecated the methods `moveToPosition`, `moveToFirstPosition`, `moveToLastPosition`,
|
||||||
|
`moveChildToPosition`, `callRecursively`, `toArray`, `getPathAsString` and `getBreadcrumbsArray`
|
||||||
|
in the MenuItem and removed them from `Knp\Menu\ItemInterface`. Use `Knp\Menu\Util\MenuManipulator`
|
||||||
|
instead.
|
||||||
|
* Made the RouterVoter compatible with SensioFrameworkExtraBundle param converters
|
||||||
|
* Added the possibility to match routes using a regex on their name in the RouterVoter
|
||||||
|
* [BC break compared to 2.0 alpha 1] Refactored the RouterVoter to make it more flexible
|
||||||
|
The way to pass routes in the item extras has changed.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```php
|
||||||
|
'extras' => array(
|
||||||
|
'routes' => array('foo', 'bar'),
|
||||||
|
'routeParameters' => array('foo' => array('id' => 4)),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```php
|
||||||
|
'extras' => array(
|
||||||
|
'routes' => array(
|
||||||
|
array('route' => 'foo', 'parameters' => array('id' => 4)),
|
||||||
|
'bar',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The old syntax is kept until the final release, but using it will trigger a E_USER_DEPRECATED error.
|
||||||
|
|
||||||
|
## 2.0.0 alpha 1 (2013-06-23)
|
||||||
|
|
||||||
|
* Added protected methods `buildOptions` and `configureItem` in the MenuFactory as extension point by inheritance
|
||||||
|
* [BC break] Refactored the way to mark items as current
|
||||||
|
``setCurrentUri``, ``getCurrentUri`` and ``getCurrentItem`` have been removed from the ItemInterface.
|
||||||
|
Determining the current items is now delegated to a matcher, and the default implementation
|
||||||
|
uses voters to apply the matching. Getting the current items can be done thanks to the CurrentItemFilterIterator.
|
||||||
|
* [BC break] The signature of the CurrentItemFilterIterator constructor changed to accept the item matcher
|
||||||
|
* [BC break] Changed the format of the breadcrumb array
|
||||||
|
Instead of storing the elements with the label as key and the uri as value
|
||||||
|
the array now stores an array of array elements with 3 keys: `label`, `uri` and `item`.
|
||||||
|
|
||||||
|
## 1.1.2 (2012-06-10)
|
||||||
|
|
||||||
|
* Updated the Silex service provider for the change in the interface
|
||||||
|
|
||||||
|
## 1.1.1 (2012-05-17)
|
||||||
|
|
||||||
|
* Added the children attributes and the extras in the array export
|
||||||
|
|
||||||
|
## 1.1.0 (2012-05-17)
|
||||||
|
|
||||||
|
* Marked `Knp\Menu\ItemInterface::getCurrentItem` as deprecated
|
||||||
|
* Added a recursive filter iterator keeping only displayed items
|
||||||
|
* Added a filter iterator keeping only current items
|
||||||
|
* Added a recursive iterator for the item
|
||||||
|
* Fixed building an array of breadcrumbs when a label has only digits
|
||||||
|
* Added a way to mark a label as safe
|
||||||
|
* Refactored the ListRenderer to be consistent with the TwigRenderer and provide the same extension points
|
||||||
|
* Added a way to attach extra data to an item
|
||||||
|
* Removed unnecessary optimization in the TwigRenderer
|
||||||
|
* Added some whitespace control in the Twig template to ensure an empty rendering is really empty
|
||||||
|
* [BC break] Use the childrenAttributes for the root instead of the attributes
|
||||||
|
* Made the default options configurable for the TwigRenderer
|
||||||
|
* Added the support for menu registered as factory in PimpleProvider
|
||||||
|
* Added a way to use the options in `knp_menu_get()` in Twig templates
|
||||||
|
* Added an array of options for the MenuProviderInterface
|
||||||
|
* Added a template to render an ordered list
|
||||||
|
* Refactored the template a bit to make it easier to use an ordered list
|
||||||
|
* Allow omitting the name of the child in `fromArray` (the key is used instead)
|
||||||
|
|
||||||
|
## 1.0.0 (2011-12-03)
|
||||||
|
|
||||||
|
* Add composer.json file
|
||||||
|
* Added more flexible list element blocks
|
||||||
|
* Add support for attributes on the children collection.
|
||||||
|
* Added a default renderer
|
||||||
|
* Added a ChainProvider for the menus.
|
||||||
|
* Added the Silex extension
|
||||||
|
* Added a RouterAwareFactory
|
||||||
|
* Added an helper to be able to reuse the logic more easily for other templating engines
|
||||||
|
* Added a way to retrieve an item using a path in a menu tree
|
||||||
|
* Changed the toArray method to use a depth instead of simply using a boolean flag
|
||||||
|
* Refactored the export to array and the creation from an array
|
||||||
|
* Added better support for encoding problems when escaping a string in the ListRenderer
|
||||||
|
* Added a Twig renderer
|
||||||
|
* Added missing escaping in the ListRenderer
|
||||||
|
* Renamed some methods in the ItemInterface
|
||||||
|
* Removed the configuration of the current item as link from the item
|
||||||
|
* Refactored the ListRenderer to use options
|
||||||
|
* Changed the interface of callRecursively
|
||||||
|
* Refactored the NodeInterface to be consistent
|
||||||
|
* Moved the creation of the item to the factory
|
||||||
|
* Added a Twig extension to render the menu easily
|
||||||
|
* Changed the menu provider interface with a pimple-based implementation
|
||||||
|
* Added a renderer provider to get a renderer by name and a Pimple-based implementation
|
||||||
|
* Removed the renderer from the menu
|
||||||
|
* Removed the num in the item by refactoring isLast and isFirst
|
||||||
|
* Changed the RendererInterface to accept an array of options to be more flexible
|
||||||
|
* Added an ItemInterface
|
||||||
|
* Initial import of KnpMenuBundle decoupled classes with a new namespace
|
19
plugins/page-toc/vendor/knplabs/knp-menu/LICENSE
vendored
Normal file
19
plugins/page-toc/vendor/knplabs/knp-menu/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2011-present KnpLabs - https://knplabs.com
|
||||||
|
|
||||||
|
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.
|
52
plugins/page-toc/vendor/knplabs/knp-menu/composer.json
vendored
Normal file
52
plugins/page-toc/vendor/knplabs/knp-menu/composer.json
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "knplabs/knp-menu",
|
||||||
|
"type": "library",
|
||||||
|
"description": "An object oriented menu library",
|
||||||
|
"keywords": ["menu", "tree"],
|
||||||
|
"homepage": "https://knplabs.com",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "KnpLabs",
|
||||||
|
"homepage": "https://knplabs.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Christophe Coevoet",
|
||||||
|
"email": "stof@notk.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "The Community",
|
||||||
|
"homepage": "https://github.com/KnpLabs/KnpMenu/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^7.3 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"twig/twig": "<1.40 || >=2,<2.9"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"psr/container": "^1.0",
|
||||||
|
"symfony/http-foundation": "^4.4 || ^5.0 || ^6.0",
|
||||||
|
"symfony/phpunit-bridge": "^5.3",
|
||||||
|
"symfony/routing": "^4.4 || ^5.0 || ^6.0",
|
||||||
|
"twig/twig": "^1.40 || ^2.9 || ^3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"twig/twig": "for the TwigRenderer and the integration with your templates"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": { "Knp\\Menu\\": "src/Knp/Menu" }
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Knp\\Menu\\Tests\\": "tests/Knp/Menu/Tests"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.3-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Factory/CoreExtension.php
vendored
Normal file
62
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Factory/CoreExtension.php
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Factory;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* core factory extension with the main logic
|
||||||
|
*/
|
||||||
|
class CoreExtension implements ExtensionInterface
|
||||||
|
{
|
||||||
|
public function buildOptions(array $options): array
|
||||||
|
{
|
||||||
|
return \array_merge(
|
||||||
|
[
|
||||||
|
'uri' => null,
|
||||||
|
'label' => null,
|
||||||
|
'attributes' => [],
|
||||||
|
'linkAttributes' => [],
|
||||||
|
'childrenAttributes' => [],
|
||||||
|
'labelAttributes' => [],
|
||||||
|
'extras' => [],
|
||||||
|
'current' => null,
|
||||||
|
'display' => true,
|
||||||
|
'displayChildren' => true,
|
||||||
|
],
|
||||||
|
$options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildItem(ItemInterface $item, array $options): void
|
||||||
|
{
|
||||||
|
$item
|
||||||
|
->setUri($options['uri'])
|
||||||
|
->setLabel($options['label'])
|
||||||
|
->setAttributes($options['attributes'])
|
||||||
|
->setLinkAttributes($options['linkAttributes'])
|
||||||
|
->setChildrenAttributes($options['childrenAttributes'])
|
||||||
|
->setLabelAttributes($options['labelAttributes'])
|
||||||
|
->setCurrent($options['current'])
|
||||||
|
->setDisplay($options['display'])
|
||||||
|
->setDisplayChildren($options['displayChildren'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->buildExtras($item, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the newly created item's extras
|
||||||
|
* Extras are processed one by one in order not to reset values set by other extensions
|
||||||
|
*
|
||||||
|
* @param array<string, array<string, mixed>> $options
|
||||||
|
*/
|
||||||
|
private function buildExtras(ItemInterface $item, array $options): void
|
||||||
|
{
|
||||||
|
if (!empty($options['extras'])) {
|
||||||
|
foreach ($options['extras'] as $key => $value) {
|
||||||
|
$item->setExtra($key, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Factory/ExtensionInterface.php
vendored
Normal file
24
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Factory/ExtensionInterface.php
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Factory;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
interface ExtensionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Builds the full option array used to configure the item.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options The options processed by the previous extensions
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function buildOptions(array $options): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the item with the passed options
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function buildItem(ItemInterface $item, array $options): void;
|
||||||
|
}
|
16
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/FactoryInterface.php
vendored
Normal file
16
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/FactoryInterface.php
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface implemented by the factory to create items
|
||||||
|
*/
|
||||||
|
interface FactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a menu item
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function createItem(string $name, array $options = []): ItemInterface;
|
||||||
|
}
|
44
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Integration/Symfony/RoutingExtension.php
vendored
Normal file
44
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Integration/Symfony/RoutingExtension.php
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Integration\Symfony;
|
||||||
|
|
||||||
|
use Knp\Menu\Factory\ExtensionInterface;
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory able to use the Symfony Routing component to build the url
|
||||||
|
*/
|
||||||
|
class RoutingExtension implements ExtensionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var UrlGeneratorInterface
|
||||||
|
*/
|
||||||
|
private $generator;
|
||||||
|
|
||||||
|
public function __construct(UrlGeneratorInterface $generator)
|
||||||
|
{
|
||||||
|
$this->generator = $generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildOptions(array $options = []): array
|
||||||
|
{
|
||||||
|
if (!empty($options['route'])) {
|
||||||
|
$params = $options['routeParameters'] ?? [];
|
||||||
|
$absolute = (isset($options['routeAbsolute']) && $options['routeAbsolute']) ? UrlGeneratorInterface::ABSOLUTE_URL : UrlGeneratorInterface::ABSOLUTE_PATH;
|
||||||
|
$options['uri'] = $this->generator->generate($options['route'], $params, $absolute);
|
||||||
|
|
||||||
|
// adding the item route to the extras under the 'routes' key (for the Silex RouteVoter)
|
||||||
|
$options['extras']['routes'][] = [
|
||||||
|
'route' => $options['route'],
|
||||||
|
'parameters' => $params,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildItem(ItemInterface $item, array $options): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
333
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/ItemInterface.php
vendored
Normal file
333
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/ItemInterface.php
vendored
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface implemented by a menu item.
|
||||||
|
*
|
||||||
|
* It roughly represents a single <li> tag and is what you should interact with
|
||||||
|
* most of the time by default.
|
||||||
|
* Originally taken from ioMenuPlugin (http://github.com/weaverryan/ioMenuPlugin)
|
||||||
|
*
|
||||||
|
* @extends \ArrayAccess<string, self|null>
|
||||||
|
* @extends \IteratorAggregate<string, self>
|
||||||
|
*/
|
||||||
|
interface ItemInterface extends \ArrayAccess, \Countable, \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function setFactory(FactoryInterface $factory): self;
|
||||||
|
|
||||||
|
public function getName(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames the item.
|
||||||
|
*
|
||||||
|
* This method must also update the key in the parent.
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if the name is already used by a sibling
|
||||||
|
*/
|
||||||
|
public function setName(string $name): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the uri for a menu item
|
||||||
|
*/
|
||||||
|
public function getUri(): ?string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the uri for a menu item
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*
|
||||||
|
* @param string|null $uri The uri to set on this menu item
|
||||||
|
*/
|
||||||
|
public function setUri(?string $uri): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the label that will be used to render this menu item
|
||||||
|
*
|
||||||
|
* Defaults to the name of no label was specified
|
||||||
|
*/
|
||||||
|
public function getLabel(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a fluent interface
|
||||||
|
*
|
||||||
|
* @param string|null $label The text to use when rendering this menu item
|
||||||
|
*/
|
||||||
|
public function setLabel(?string $label): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
public function getAttributes(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string|bool|null> $attributes
|
||||||
|
*/
|
||||||
|
public function setAttributes(array $attributes): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name The name of the attribute to return
|
||||||
|
* @param string|bool|null $default The value to return if the attribute doesn't exist
|
||||||
|
*
|
||||||
|
* @return string|bool|null
|
||||||
|
*/
|
||||||
|
public function getAttribute(string $name, $default = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|bool|null $value
|
||||||
|
*/
|
||||||
|
public function setAttribute(string $name, $value): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
public function getLinkAttributes(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string|bool|null> $linkAttributes
|
||||||
|
*/
|
||||||
|
public function setLinkAttributes(array $linkAttributes): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name The name of the attribute to return
|
||||||
|
* @param string|bool|null $default The value to return if the attribute doesn't exist
|
||||||
|
*
|
||||||
|
* @return string|bool|null
|
||||||
|
*/
|
||||||
|
public function getLinkAttribute(string $name, $default = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|bool|null $value
|
||||||
|
*/
|
||||||
|
public function setLinkAttribute(string $name, $value): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
public function getChildrenAttributes(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string|bool|null> $childrenAttributes
|
||||||
|
*/
|
||||||
|
public function setChildrenAttributes(array $childrenAttributes): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name The name of the attribute to return
|
||||||
|
* @param string|bool|null $default The value to return if the attribute doesn't exist
|
||||||
|
*
|
||||||
|
* @return string|bool|null
|
||||||
|
*/
|
||||||
|
public function getChildrenAttribute(string $name, $default = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|bool|null $value
|
||||||
|
*/
|
||||||
|
public function setChildrenAttribute(string $name, $value): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
public function getLabelAttributes(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string|bool|null> $labelAttributes
|
||||||
|
*/
|
||||||
|
public function setLabelAttributes(array $labelAttributes): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name The name of the attribute to return
|
||||||
|
* @param string|bool|null $default The value to return if the attribute doesn't exist
|
||||||
|
*
|
||||||
|
* @return string|bool|null
|
||||||
|
*/
|
||||||
|
public function getLabelAttribute(string $name, $default = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|bool|null $value
|
||||||
|
*/
|
||||||
|
public function setLabelAttribute(string $name, $value): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function getExtras(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $extras
|
||||||
|
*/
|
||||||
|
public function setExtras(array $extras): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name The name of the extra to return
|
||||||
|
* @param mixed $default The value to return if the extra doesn't exist
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getExtra(string $name, $default = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function setExtra(string $name, $value): self;
|
||||||
|
|
||||||
|
public function getDisplayChildren(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether or not this menu item should show its children
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*/
|
||||||
|
public function setDisplayChildren(bool $bool): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to display this menu item
|
||||||
|
*/
|
||||||
|
public function isDisplayed(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether or not this menu should be displayed
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*/
|
||||||
|
public function setDisplay(bool $bool): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a child menu item to this menu
|
||||||
|
*
|
||||||
|
* Returns the child item
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string $child An ItemInterface instance or the name of a new item to create
|
||||||
|
* @param array<string, mixed> $options If creating a new item, the options passed to the factory for the item
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if the item is already in a tree
|
||||||
|
*/
|
||||||
|
public function addChild($child, array $options = []): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the child menu identified by the given name
|
||||||
|
*
|
||||||
|
* @param string $name Then name of the child menu to return
|
||||||
|
*/
|
||||||
|
public function getChild(string $name): ?self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder children.
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*
|
||||||
|
* @param array<int|string, string> $order new order of children
|
||||||
|
*/
|
||||||
|
public function reorderChildren(array $order): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a deep copy of menu tree. Every item is copied as another object.
|
||||||
|
*/
|
||||||
|
public function copy(): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the level of this menu item
|
||||||
|
*
|
||||||
|
* The root menu item is 0, followed by 1, 2, etc
|
||||||
|
*/
|
||||||
|
public function getLevel(): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root ItemInterface of this menu tree
|
||||||
|
*/
|
||||||
|
public function getRoot(): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not this menu item is the root menu item
|
||||||
|
*/
|
||||||
|
public function isRoot(): bool;
|
||||||
|
|
||||||
|
public function getParent(): ?self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used internally when adding and removing children
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*/
|
||||||
|
public function setParent(?self $parent = null): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the children as an array of ItemInterface objects
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public function getChildren(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a fluent interface
|
||||||
|
*
|
||||||
|
* @param array<string, self> $children An array of ItemInterface objects
|
||||||
|
*/
|
||||||
|
public function setChildren(array $children): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a child from this menu item
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string $name The name of ItemInterface instance or the ItemInterface to remove
|
||||||
|
*/
|
||||||
|
public function removeChild($name): self;
|
||||||
|
|
||||||
|
public function getFirstChild(): self;
|
||||||
|
|
||||||
|
public function getLastChild(): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not this menu items has viewable children
|
||||||
|
*
|
||||||
|
* This menu MAY have children, but this will return false if the current
|
||||||
|
* user does not have access to view any of those items
|
||||||
|
*/
|
||||||
|
public function hasChildren(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not this menu item is "current".
|
||||||
|
*
|
||||||
|
* If the state is unknown, use null.
|
||||||
|
*
|
||||||
|
* Provides a fluent interface
|
||||||
|
*
|
||||||
|
* @param bool|null $bool Specify that this menu item is current
|
||||||
|
*/
|
||||||
|
public function setCurrent(?bool $bool): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether or not this menu item is "current".
|
||||||
|
*/
|
||||||
|
public function isCurrent(): ?bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this menu item is last in its parent
|
||||||
|
*/
|
||||||
|
public function isLast(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this menu item is first in its parent
|
||||||
|
*/
|
||||||
|
public function isFirst(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whereas isFirst() returns if this is the first child of the parent
|
||||||
|
* menu item, this function takes into consideration whether children are rendered or not.
|
||||||
|
*
|
||||||
|
* This returns true if this is the first child that would be rendered
|
||||||
|
* for the current user
|
||||||
|
*/
|
||||||
|
public function actsLikeFirst(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whereas isLast() returns if this is the last child of the parent
|
||||||
|
* menu item, this function takes into consideration whether children are rendered or not.
|
||||||
|
*
|
||||||
|
* This returns true if this is the last child that would be rendered
|
||||||
|
* for the current user
|
||||||
|
*/
|
||||||
|
public function actsLikeLast(): bool;
|
||||||
|
}
|
35
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Iterator/CurrentItemFilterIterator.php
vendored
Normal file
35
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Iterator/CurrentItemFilterIterator.php
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Iterator;
|
||||||
|
|
||||||
|
use Knp\Menu\Matcher\MatcherInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter iterator keeping only current items
|
||||||
|
*/
|
||||||
|
class CurrentItemFilterIterator extends \FilterIterator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var MatcherInterface
|
||||||
|
*/
|
||||||
|
private $matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Iterator<string|int, \Knp\Menu\ItemInterface> $iterator
|
||||||
|
*/
|
||||||
|
public function __construct(\Iterator $iterator, MatcherInterface $matcher)
|
||||||
|
{
|
||||||
|
$this->matcher = $matcher;
|
||||||
|
|
||||||
|
parent::__construct($iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function accept()
|
||||||
|
{
|
||||||
|
return $this->matcher->isCurrent($this->current());
|
||||||
|
}
|
||||||
|
}
|
27
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Iterator/DisplayedItemFilterIterator.php
vendored
Normal file
27
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Iterator/DisplayedItemFilterIterator.php
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter iterator keeping only current items
|
||||||
|
*/
|
||||||
|
class DisplayedItemFilterIterator extends \RecursiveFilterIterator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function accept()
|
||||||
|
{
|
||||||
|
return $this->current()->isDisplayed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function hasChildren()
|
||||||
|
{
|
||||||
|
return $this->current()->getDisplayChildren() && parent::hasChildren();
|
||||||
|
}
|
||||||
|
}
|
33
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Iterator/RecursiveItemIterator.php
vendored
Normal file
33
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Iterator/RecursiveItemIterator.php
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive iterator iterating on an item
|
||||||
|
*
|
||||||
|
* @extends \IteratorIterator<string, \Knp\Menu\ItemInterface, \Traversable<string, \Knp\Menu\ItemInterface>>
|
||||||
|
*/
|
||||||
|
class RecursiveItemIterator extends \IteratorIterator implements \RecursiveIterator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param \Traversable<string, \Knp\Menu\ItemInterface> $iterator
|
||||||
|
*/
|
||||||
|
final public function __construct(\Traversable $iterator)
|
||||||
|
{
|
||||||
|
parent::__construct($iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChildren(): bool
|
||||||
|
{
|
||||||
|
return 0 < \count($this->current());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \RecursiveIterator
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
return new static($this->current());
|
||||||
|
}
|
||||||
|
}
|
62
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Loader/ArrayLoader.php
vendored
Normal file
62
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Loader/ArrayLoader.php
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Loader;
|
||||||
|
|
||||||
|
use Knp\Menu\FactoryInterface;
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loader importing a menu tree from an array.
|
||||||
|
*
|
||||||
|
* The array should match the output of MenuManipulator::toArray
|
||||||
|
*/
|
||||||
|
class ArrayLoader implements LoaderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var FactoryInterface
|
||||||
|
*/
|
||||||
|
private $factory;
|
||||||
|
|
||||||
|
public function __construct(FactoryInterface $factory)
|
||||||
|
{
|
||||||
|
$this->factory = $factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load($data): ItemInterface
|
||||||
|
{
|
||||||
|
if (!$this->supports($data)) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('Unsupported data. Expected an array but got %s', \is_object($data) ? \get_class($data) : \gettype($data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fromArray($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports($data): bool
|
||||||
|
{
|
||||||
|
return \is_array($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
* @param string|null $name (the name of the item, used only if there is no name in the data themselves)
|
||||||
|
*/
|
||||||
|
private function fromArray(array $data, ?string $name = null): ItemInterface
|
||||||
|
{
|
||||||
|
$name = $data['name'] ?? $name;
|
||||||
|
|
||||||
|
if (isset($data['children'])) {
|
||||||
|
$children = $data['children'];
|
||||||
|
unset($data['children']);
|
||||||
|
} else {
|
||||||
|
$children = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->factory->createItem($name, $data);
|
||||||
|
|
||||||
|
foreach ($children as $childName => $child) {
|
||||||
|
$item->addChild($this->fromArray($child, $childName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
}
|
22
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Loader/LoaderInterface.php
vendored
Normal file
22
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Loader/LoaderInterface.php
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Loader;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
interface LoaderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Loads the data into a menu item
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
*/
|
||||||
|
public function load($data): ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the loader can load these data
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
*/
|
||||||
|
public function supports($data): bool;
|
||||||
|
}
|
40
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Loader/NodeLoader.php
vendored
Normal file
40
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Loader/NodeLoader.php
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Loader;
|
||||||
|
|
||||||
|
use Knp\Menu\FactoryInterface;
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\NodeInterface;
|
||||||
|
|
||||||
|
class NodeLoader implements LoaderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var FactoryInterface
|
||||||
|
*/
|
||||||
|
private $factory;
|
||||||
|
|
||||||
|
public function __construct(FactoryInterface $factory)
|
||||||
|
{
|
||||||
|
$this->factory = $factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load($data): ItemInterface
|
||||||
|
{
|
||||||
|
if (!$data instanceof NodeInterface) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('Unsupported data. Expected Knp\Menu\NodeInterface but got %s', \is_object($data) ? \get_class($data) : \gettype($data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->factory->createItem($data->getName(), $data->getOptions());
|
||||||
|
|
||||||
|
foreach ($data->getChildren() as $childNode) {
|
||||||
|
$item->addChild($this->load($childNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports($data): bool
|
||||||
|
{
|
||||||
|
return $data instanceof NodeInterface;
|
||||||
|
}
|
||||||
|
}
|
76
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Matcher.php
vendored
Normal file
76
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Matcher.php
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Matcher;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\Matcher\Voter\VoterInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A MatcherInterface implementation using a voter system
|
||||||
|
*/
|
||||||
|
class Matcher implements MatcherInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \SplObjectStorage<ItemInterface, bool>
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var iterable|VoterInterface[]
|
||||||
|
*/
|
||||||
|
private $voters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param VoterInterface[]|iterable $voters
|
||||||
|
*/
|
||||||
|
public function __construct($voters = [])
|
||||||
|
{
|
||||||
|
$this->voters = $voters;
|
||||||
|
$this->cache = new \SplObjectStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCurrent(ItemInterface $item): bool
|
||||||
|
{
|
||||||
|
$current = $item->isCurrent();
|
||||||
|
if (null !== $current) {
|
||||||
|
return $current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->cache->contains($item)) {
|
||||||
|
return $this->cache[$item];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->voters as $voter) {
|
||||||
|
$current = $voter->matchItem($item);
|
||||||
|
if (null !== $current) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = (bool) $current;
|
||||||
|
$this->cache[$item] = $current;
|
||||||
|
|
||||||
|
return $current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAncestor(ItemInterface $item, ?int $depth = null): bool
|
||||||
|
{
|
||||||
|
if (0 === $depth) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$childDepth = null === $depth ? null : $depth - 1;
|
||||||
|
foreach ($item->getChildren() as $child) {
|
||||||
|
if ($this->isCurrent($child) || $this->isAncestor($child, $childDepth)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clear(): void
|
||||||
|
{
|
||||||
|
$this->cache = new \SplObjectStorage();
|
||||||
|
}
|
||||||
|
}
|
28
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/MatcherInterface.php
vendored
Normal file
28
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/MatcherInterface.php
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Matcher;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface implemented by the item matcher
|
||||||
|
*/
|
||||||
|
interface MatcherInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Checks whether an item is current.
|
||||||
|
*/
|
||||||
|
public function isCurrent(ItemInterface $item): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an item is the ancestor of a current item.
|
||||||
|
*
|
||||||
|
* @param int|null $depth The max depth to look for the item
|
||||||
|
*/
|
||||||
|
public function isAncestor(ItemInterface $item, ?int $depth = null): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the state of the matcher.
|
||||||
|
*/
|
||||||
|
public function clear(): void;
|
||||||
|
}
|
35
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/RegexVoter.php
vendored
Normal file
35
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/RegexVoter.php
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Matcher\Voter;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the VoterInterface which can be used as voter for "current" class
|
||||||
|
* `matchItem` will return true if the pattern you're searching for is found in the URI of the item
|
||||||
|
*/
|
||||||
|
class RegexVoter implements VoterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $regexp;
|
||||||
|
|
||||||
|
public function __construct(?string $regexp)
|
||||||
|
{
|
||||||
|
$this->regexp = $regexp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function matchItem(ItemInterface $item): ?bool
|
||||||
|
{
|
||||||
|
if (null === $this->regexp || null === $item->getUri()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\preg_match($this->regexp, $item->getUri())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
94
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/RouteVoter.php
vendored
Normal file
94
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/RouteVoter.php
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Matcher\Voter;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Voter based on the route
|
||||||
|
*/
|
||||||
|
class RouteVoter implements VoterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var RequestStack
|
||||||
|
*/
|
||||||
|
private $requestStack;
|
||||||
|
|
||||||
|
public function __construct(RequestStack $requestStack)
|
||||||
|
{
|
||||||
|
$this->requestStack = $requestStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function matchItem(ItemInterface $item): ?bool
|
||||||
|
{
|
||||||
|
if (\is_callable([$this->requestStack, 'getMainRequest'])) {
|
||||||
|
$request = $this->requestStack->getMainRequest(); // symfony 5.3+
|
||||||
|
} else {
|
||||||
|
$request = $this->requestStack->getMasterRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$route = $request->attributes->get('_route');
|
||||||
|
if (null === $route) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$routes = (array) $item->getExtra('routes', []);
|
||||||
|
|
||||||
|
foreach ($routes as $testedRoute) {
|
||||||
|
if (\is_string($testedRoute)) {
|
||||||
|
$testedRoute = ['route' => $testedRoute];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_array($testedRoute)) {
|
||||||
|
throw new \InvalidArgumentException('Routes extra items must be strings or arrays.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isMatchingRoute($request, $testedRoute)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-param array{route?: string|null, pattern?: string|null, parameters?: array<string, mixed>} $testedRoute
|
||||||
|
*/
|
||||||
|
private function isMatchingRoute(Request $request, array $testedRoute): bool
|
||||||
|
{
|
||||||
|
$route = $request->attributes->get('_route');
|
||||||
|
|
||||||
|
if (isset($testedRoute['route'])) {
|
||||||
|
if ($route !== $testedRoute['route']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} elseif (!empty($testedRoute['pattern'])) {
|
||||||
|
if (!\preg_match($testedRoute['pattern'], $route)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \InvalidArgumentException('Routes extra items must have a "route" or "pattern" key.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($testedRoute['parameters'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$routeParameters = $request->attributes->get('_route_params', []);
|
||||||
|
|
||||||
|
foreach ($testedRoute['parameters'] as $name => $value) {
|
||||||
|
// cast both to string so that we handle integer and other non-string parameters, but don't stumble on 0 == 'abc'.
|
||||||
|
if (!isset($routeParameters[$name]) || (string) $routeParameters[$name] !== (string) $value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
34
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/UriVoter.php
vendored
Normal file
34
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/UriVoter.php
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Matcher\Voter;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Voter based on the uri
|
||||||
|
*/
|
||||||
|
class UriVoter implements VoterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $uri;
|
||||||
|
|
||||||
|
public function __construct(?string $uri = null)
|
||||||
|
{
|
||||||
|
$this->uri = $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function matchItem(ItemInterface $item): ?bool
|
||||||
|
{
|
||||||
|
if (null === $this->uri || null === $item->getUri()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->getUri() === $this->uri) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
19
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/VoterInterface.php
vendored
Normal file
19
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Matcher/Voter/VoterInterface.php
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Matcher\Voter;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface implemented by the matching voters
|
||||||
|
*/
|
||||||
|
interface VoterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Checks whether an item is current.
|
||||||
|
*
|
||||||
|
* If the voter is not able to determine a result,
|
||||||
|
* it should return null to let other voters do the job.
|
||||||
|
*/
|
||||||
|
public function matchItem(ItemInterface $item): ?bool;
|
||||||
|
}
|
66
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/MenuFactory.php
vendored
Normal file
66
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/MenuFactory.php
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu;
|
||||||
|
|
||||||
|
use Knp\Menu\Factory\CoreExtension;
|
||||||
|
use Knp\Menu\Factory\ExtensionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory to create a menu from a tree
|
||||||
|
*/
|
||||||
|
class MenuFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
*/
|
||||||
|
private $extensions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtensionInterface[]|null
|
||||||
|
*/
|
||||||
|
private $sorted;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->addExtension(new CoreExtension(), -10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createItem(string $name, array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
foreach ($this->getExtensions() as $extension) {
|
||||||
|
$options = $extension->buildOptions($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = new MenuItem($name, $this);
|
||||||
|
|
||||||
|
foreach ($this->getExtensions() as $extension) {
|
||||||
|
$extension->buildItem($item, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a factory extension
|
||||||
|
*/
|
||||||
|
public function addExtension(ExtensionInterface $extension, int $priority = 0): void
|
||||||
|
{
|
||||||
|
$this->extensions[$priority][] = $extension;
|
||||||
|
$this->sorted = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the internal list of extensions by priority.
|
||||||
|
*
|
||||||
|
* @return ExtensionInterface[]
|
||||||
|
*/
|
||||||
|
private function getExtensions(): array
|
||||||
|
{
|
||||||
|
if (null === $this->sorted) {
|
||||||
|
\krsort($this->extensions);
|
||||||
|
$this->sorted = !empty($this->extensions) ? \array_merge(...$this->extensions) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->sorted;
|
||||||
|
}
|
||||||
|
}
|
620
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/MenuItem.php
vendored
Normal file
620
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/MenuItem.php
vendored
Normal file
@ -0,0 +1,620 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the ItemInterface
|
||||||
|
*/
|
||||||
|
class MenuItem implements ItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Name of this menu item (used for id by parent menu)
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label to output, name is used by default
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributes for the item link
|
||||||
|
*
|
||||||
|
* @var array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
protected $linkAttributes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributes for the children list
|
||||||
|
*
|
||||||
|
* @var array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
protected $childrenAttributes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributes for the item text
|
||||||
|
*
|
||||||
|
* @var array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
protected $labelAttributes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uri to use in the anchor tag
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributes for the item
|
||||||
|
*
|
||||||
|
* @var array<string, string|bool|null>
|
||||||
|
*/
|
||||||
|
protected $attributes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra stuff associated to the item
|
||||||
|
*
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
|
protected $extras = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the item is displayed
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $display = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the children of the item are displayed
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $displayChildren = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child items
|
||||||
|
*
|
||||||
|
* @var array<string, ItemInterface>
|
||||||
|
*/
|
||||||
|
protected $children = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent item
|
||||||
|
*
|
||||||
|
* @var ItemInterface|null
|
||||||
|
*/
|
||||||
|
protected $parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether the item is current. null means unknown
|
||||||
|
*
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
protected $isCurrent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FactoryInterface
|
||||||
|
*/
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor
|
||||||
|
*
|
||||||
|
* @param string $name The name of this menu, which is how its parent will
|
||||||
|
* reference it. Also used as label if label not specified
|
||||||
|
*/
|
||||||
|
public function __construct(string $name, FactoryInterface $factory)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->factory = $factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFactory(FactoryInterface $factory): ItemInterface
|
||||||
|
{
|
||||||
|
$this->factory = $factory;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(string $name): ItemInterface
|
||||||
|
{
|
||||||
|
if ($this->name === $name) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent = $this->getParent();
|
||||||
|
if (null !== $parent && isset($parent[$name])) {
|
||||||
|
throw new \InvalidArgumentException('Cannot rename item, name is already used by sibling.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$oldName = $this->name;
|
||||||
|
$this->name = $name;
|
||||||
|
|
||||||
|
if (null !== $parent) {
|
||||||
|
$names = \array_keys($parent->getChildren());
|
||||||
|
$items = \array_values($parent->getChildren());
|
||||||
|
|
||||||
|
$offset = \array_search($oldName, $names);
|
||||||
|
$names[$offset] = $name;
|
||||||
|
|
||||||
|
if (false === $children = \array_combine($names, $items)) {
|
||||||
|
throw new \InvalidArgumentException('Number of elements is not matching.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent->setChildren($children);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUri(): ?string
|
||||||
|
{
|
||||||
|
return $this->uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUri(?string $uri): ItemInterface
|
||||||
|
{
|
||||||
|
$this->uri = $uri;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabel(): string
|
||||||
|
{
|
||||||
|
return $this->label ?? $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLabel(?string $label): ItemInterface
|
||||||
|
{
|
||||||
|
$this->label = $label;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttributes(): array
|
||||||
|
{
|
||||||
|
return $this->attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAttributes(array $attributes): ItemInterface
|
||||||
|
{
|
||||||
|
$this->attributes = $attributes;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttribute(string $name, $default = null)
|
||||||
|
{
|
||||||
|
return $this->attributes[$name] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAttribute(string $name, $value): ItemInterface
|
||||||
|
{
|
||||||
|
$this->attributes[$name] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLinkAttributes(): array
|
||||||
|
{
|
||||||
|
return $this->linkAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLinkAttributes(array $linkAttributes): ItemInterface
|
||||||
|
{
|
||||||
|
$this->linkAttributes = $linkAttributes;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLinkAttribute(string $name, $default = null)
|
||||||
|
{
|
||||||
|
return $this->linkAttributes[$name] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLinkAttribute(string $name, $value): ItemInterface
|
||||||
|
{
|
||||||
|
$this->linkAttributes[$name] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChildrenAttributes(): array
|
||||||
|
{
|
||||||
|
return $this->childrenAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChildrenAttributes(array $childrenAttributes): ItemInterface
|
||||||
|
{
|
||||||
|
$this->childrenAttributes = $childrenAttributes;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChildrenAttribute(string $name, $default = null)
|
||||||
|
{
|
||||||
|
return $this->childrenAttributes[$name] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChildrenAttribute(string $name, $value): ItemInterface
|
||||||
|
{
|
||||||
|
$this->childrenAttributes[$name] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabelAttributes(): array
|
||||||
|
{
|
||||||
|
return $this->labelAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLabelAttributes(array $labelAttributes): ItemInterface
|
||||||
|
{
|
||||||
|
$this->labelAttributes = $labelAttributes;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabelAttribute(string $name, $default = null)
|
||||||
|
{
|
||||||
|
return $this->labelAttributes[$name] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLabelAttribute(string $name, $value): ItemInterface
|
||||||
|
{
|
||||||
|
$this->labelAttributes[$name] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtras(): array
|
||||||
|
{
|
||||||
|
return $this->extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExtras(array $extras): ItemInterface
|
||||||
|
{
|
||||||
|
$this->extras = $extras;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtra(string $name, $default = null)
|
||||||
|
{
|
||||||
|
return $this->extras[$name] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExtra(string $name, $value): ItemInterface
|
||||||
|
{
|
||||||
|
$this->extras[$name] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayChildren(): bool
|
||||||
|
{
|
||||||
|
return $this->displayChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDisplayChildren(bool $bool): ItemInterface
|
||||||
|
{
|
||||||
|
$this->displayChildren = $bool;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDisplayed(): bool
|
||||||
|
{
|
||||||
|
return $this->display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDisplay(bool $bool): ItemInterface
|
||||||
|
{
|
||||||
|
$this->display = $bool;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addChild($child, array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
if (!$child instanceof ItemInterface) {
|
||||||
|
$child = $this->factory->createItem($child, $options);
|
||||||
|
} elseif (null !== $child->getParent()) {
|
||||||
|
throw new \InvalidArgumentException('Cannot add menu item as child, it already belongs to another menu (e.g. has a parent).');
|
||||||
|
}
|
||||||
|
|
||||||
|
$child->setParent($this);
|
||||||
|
|
||||||
|
$this->children[$child->getName()] = $child;
|
||||||
|
|
||||||
|
return $child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChild(string $name): ?ItemInterface
|
||||||
|
{
|
||||||
|
return $this->children[$name] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reorderChildren(array $order): ItemInterface
|
||||||
|
{
|
||||||
|
if (\count($order) !== $this->count()) {
|
||||||
|
throw new \InvalidArgumentException('Cannot reorder children, order does not contain all children.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$newChildren = [];
|
||||||
|
|
||||||
|
foreach ($order as $name) {
|
||||||
|
if (!isset($this->children[$name])) {
|
||||||
|
throw new \InvalidArgumentException('Cannot find children named '.$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
$child = $this->children[$name];
|
||||||
|
$newChildren[$name] = $child;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setChildren($newChildren);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function copy(): ItemInterface
|
||||||
|
{
|
||||||
|
$newMenu = clone $this;
|
||||||
|
$newMenu->setChildren([]);
|
||||||
|
$newMenu->setParent();
|
||||||
|
foreach ($this->getChildren() as $child) {
|
||||||
|
$newMenu->addChild($child->copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLevel(): int
|
||||||
|
{
|
||||||
|
return $this->parent ? $this->parent->getLevel() + 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRoot(): ItemInterface
|
||||||
|
{
|
||||||
|
$obj = $this;
|
||||||
|
do {
|
||||||
|
$found = $obj;
|
||||||
|
} while ($obj = $obj->getParent());
|
||||||
|
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRoot(): bool
|
||||||
|
{
|
||||||
|
return null === $this->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent(): ?ItemInterface
|
||||||
|
{
|
||||||
|
return $this->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setParent(?ItemInterface $parent = null): ItemInterface
|
||||||
|
{
|
||||||
|
if ($parent === $this) {
|
||||||
|
throw new \InvalidArgumentException('Item cannot be a child of itself');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->parent = $parent;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChildren(): array
|
||||||
|
{
|
||||||
|
return $this->children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChildren(array $children): ItemInterface
|
||||||
|
{
|
||||||
|
$this->children = $children;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeChild($name): ItemInterface
|
||||||
|
{
|
||||||
|
$name = $name instanceof ItemInterface ? $name->getName() : $name;
|
||||||
|
|
||||||
|
if (isset($this->children[$name])) {
|
||||||
|
// unset the child and reset it so it looks independent
|
||||||
|
$this->children[$name]->setParent(null);
|
||||||
|
unset($this->children[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFirstChild(): ItemInterface
|
||||||
|
{
|
||||||
|
if (empty($this->children)) {
|
||||||
|
throw new \LogicException('Cannot get first child: there are no children.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return \reset($this->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLastChild(): ItemInterface
|
||||||
|
{
|
||||||
|
if (empty($this->children)) {
|
||||||
|
throw new \LogicException('Cannot get last child: there are no children.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return \end($this->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChildren(): bool
|
||||||
|
{
|
||||||
|
foreach ($this->children as $child) {
|
||||||
|
if ($child->isDisplayed()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrent(?bool $bool): ItemInterface
|
||||||
|
{
|
||||||
|
$this->isCurrent = $bool;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCurrent(): ?bool
|
||||||
|
{
|
||||||
|
return $this->isCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLast(): bool
|
||||||
|
{
|
||||||
|
// if this is root, then return false
|
||||||
|
if (null === $this->parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->parent->getLastChild() === $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isFirst(): bool
|
||||||
|
{
|
||||||
|
// if this is root, then return false
|
||||||
|
if (null === $this->parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->parent->getFirstChild() === $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actsLikeFirst(): bool
|
||||||
|
{
|
||||||
|
// root items are never "marked" as first
|
||||||
|
if (null === $this->parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A menu acts like first only if it is displayed
|
||||||
|
if (!$this->isDisplayed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're first and visible, we're first, period.
|
||||||
|
if ($this->isFirst()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$children = $this->parent->getChildren();
|
||||||
|
foreach ($children as $child) {
|
||||||
|
// loop until we find a visible menu. If its this menu, we're first
|
||||||
|
if ($child->isDisplayed()) {
|
||||||
|
return $child->getName() === $this->getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actsLikeLast(): bool
|
||||||
|
{
|
||||||
|
// root items are never "marked" as last
|
||||||
|
if (null === $this->parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A menu acts like last only if it is displayed
|
||||||
|
if (!$this->isDisplayed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're last and visible, we're last, period.
|
||||||
|
if ($this->isLast()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$children = \array_reverse($this->parent->getChildren());
|
||||||
|
foreach ($children as $child) {
|
||||||
|
// loop until we find a visible menu. If its this menu, we're first
|
||||||
|
if ($child->isDisplayed()) {
|
||||||
|
return $child->getName() === $this->getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Countable
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return \count($this->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements IteratorAggregate
|
||||||
|
*/
|
||||||
|
public function getIterator(): \Traversable
|
||||||
|
{
|
||||||
|
return new \ArrayIterator($this->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements ArrayAccess
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
*/
|
||||||
|
public function offsetExists($offset): bool
|
||||||
|
{
|
||||||
|
return isset($this->children[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements ArrayAccess
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
*
|
||||||
|
* @return ItemInterface|null
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
return $this->getChild($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements ArrayAccess
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
* @param string|null $value
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset, $value): void
|
||||||
|
{
|
||||||
|
$this->addChild($offset)->setLabel($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements ArrayAccess
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
*/
|
||||||
|
public function offsetUnset($offset): void
|
||||||
|
{
|
||||||
|
$this->removeChild($offset);
|
||||||
|
}
|
||||||
|
}
|
30
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/NodeInterface.php
vendored
Normal file
30
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/NodeInterface.php
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface implemented by a node to construct a menu from a tree.
|
||||||
|
*/
|
||||||
|
interface NodeInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the name of the node
|
||||||
|
*
|
||||||
|
* Each child of a node must have a unique name
|
||||||
|
*/
|
||||||
|
public function getName(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the options for the factory to create the item for this node
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function getOptions(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the child nodes implementing NodeInterface
|
||||||
|
*
|
||||||
|
* @return \Traversable<int, self>
|
||||||
|
*/
|
||||||
|
public function getChildren(): \Traversable;
|
||||||
|
}
|
55
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/ArrayAccessProvider.php
vendored
Normal file
55
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/ArrayAccessProvider.php
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Provider;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A menu provider getting the menus from a class implementing ArrayAccess.
|
||||||
|
*
|
||||||
|
* In case the value stored in the registry is a callable rather than an ItemInterface,
|
||||||
|
* it will be called with the options as first argument and the registry as second argument
|
||||||
|
* and is expected to return a menu item.
|
||||||
|
*/
|
||||||
|
class ArrayAccessProvider implements MenuProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \ArrayAccess<string, ItemInterface|callable>
|
||||||
|
*/
|
||||||
|
private $registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $menuIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \ArrayAccess<string, ItemInterface|callable> $registry
|
||||||
|
* @param array<string, string> $menuIds The map between menu identifiers and registry keys
|
||||||
|
*/
|
||||||
|
public function __construct(\ArrayAccess $registry, array $menuIds = [])
|
||||||
|
{
|
||||||
|
$this->registry = $registry;
|
||||||
|
$this->menuIds = $menuIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $name, array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
if (!isset($this->menuIds[$name])) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('The menu "%s" is not defined.', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$menu = $this->registry[$this->menuIds[$name]];
|
||||||
|
|
||||||
|
if (\is_callable($menu)) {
|
||||||
|
$menu = $menu($options, $this->registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function has(string $name, array $options = []): bool
|
||||||
|
{
|
||||||
|
return isset($this->menuIds[$name]);
|
||||||
|
}
|
||||||
|
}
|
43
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/ChainProvider.php
vendored
Normal file
43
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/ChainProvider.php
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Provider;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
class ChainProvider implements MenuProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var iterable|MenuProviderInterface[]
|
||||||
|
*/
|
||||||
|
private $providers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param MenuProviderInterface[]|iterable $providers
|
||||||
|
*/
|
||||||
|
public function __construct($providers)
|
||||||
|
{
|
||||||
|
$this->providers = $providers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $name, array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
foreach ($this->providers as $provider) {
|
||||||
|
if ($provider->has($name, $options)) {
|
||||||
|
return $provider->get($name, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(\sprintf('The menu "%s" is not defined.', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function has(string $name, array $options = []): bool
|
||||||
|
{
|
||||||
|
foreach ($this->providers as $provider) {
|
||||||
|
if ($provider->has($name, $options)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
52
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/LazyProvider.php
vendored
Normal file
52
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/LazyProvider.php
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Provider;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A menu provider building menus lazily thanks to builder callables.
|
||||||
|
*
|
||||||
|
* Builders can either be callables or a factory for an object callable
|
||||||
|
* represented as [Closure, method], where the Closure gets called
|
||||||
|
* to instantiate the object.
|
||||||
|
*/
|
||||||
|
class LazyProvider implements MenuProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
|
private $builders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-param array<string, callable|array{\Closure, string}> $builders
|
||||||
|
*/
|
||||||
|
public function __construct(array $builders)
|
||||||
|
{
|
||||||
|
$this->builders = $builders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $name, array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
if (!isset($this->builders[$name])) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('The menu "%s" is not defined.', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$builder = $this->builders[$name];
|
||||||
|
|
||||||
|
if (\is_array($builder) && isset($builder[0]) && $builder[0] instanceof \Closure) {
|
||||||
|
$builder[0] = $builder[0]();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_callable($builder)) {
|
||||||
|
throw new \LogicException(\sprintf('Invalid menu builder for "%s". A callable or a factory for an object callable are expected.', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $builder($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function has(string $name, array $options = []): bool
|
||||||
|
{
|
||||||
|
return isset($this->builders[$name]);
|
||||||
|
}
|
||||||
|
}
|
24
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/MenuProviderInterface.php
vendored
Normal file
24
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/MenuProviderInterface.php
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Provider;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
interface MenuProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieves a menu by its name
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if the menu does not exists
|
||||||
|
*/
|
||||||
|
public function get(string $name, array $options = []): ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a menu exists in this provider
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function has(string $name, array $options = []): bool;
|
||||||
|
}
|
39
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/PsrProvider.php
vendored
Normal file
39
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Provider/PsrProvider.php
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Provider;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A menu provider getting the menus from a PSR-11 container.
|
||||||
|
*
|
||||||
|
* This menu provider does not support using options, as it cannot pass them to the container
|
||||||
|
* to alter the menu building. Use a different provider in case you need support for options.
|
||||||
|
*/
|
||||||
|
class PsrProvider implements MenuProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $name, array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
if (!$this->container->has($name)) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('The menu "%s" is not defined.', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->container->get($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function has(string $name, array $options = []): bool
|
||||||
|
{
|
||||||
|
return $this->container->has($name);
|
||||||
|
}
|
||||||
|
}
|
54
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/ArrayAccessProvider.php
vendored
Normal file
54
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/ArrayAccessProvider.php
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Renderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A renderer provider getting the renderers from a class implementing ArrayAccess.
|
||||||
|
*/
|
||||||
|
class ArrayAccessProvider implements RendererProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \ArrayAccess<string, RendererInterface>
|
||||||
|
*/
|
||||||
|
private $registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $rendererIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $defaultRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \ArrayAccess<string, RendererInterface> $registry
|
||||||
|
* @param string $defaultRenderer The name of the renderer used by default
|
||||||
|
* @param array<string, string> $rendererIds The map between renderer names and registry keys
|
||||||
|
*/
|
||||||
|
public function __construct(\ArrayAccess $registry, string $defaultRenderer, array $rendererIds)
|
||||||
|
{
|
||||||
|
$this->registry = $registry;
|
||||||
|
$this->rendererIds = $rendererIds;
|
||||||
|
$this->defaultRenderer = $defaultRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(?string $name = null): RendererInterface
|
||||||
|
{
|
||||||
|
if (null === $name) {
|
||||||
|
$name = $this->defaultRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->rendererIds[$name]) || null === $this->registry[$this->rendererIds[$name]]) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('The renderer "%s" is not defined.', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->registry[$this->rendererIds[$name]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function has(string $name): bool
|
||||||
|
{
|
||||||
|
return isset($this->rendererIds[$name]);
|
||||||
|
}
|
||||||
|
}
|
260
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/ListRenderer.php
vendored
Normal file
260
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/ListRenderer.php
vendored
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Renderer;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\Matcher\MatcherInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders MenuItem tree as unordered list
|
||||||
|
*/
|
||||||
|
class ListRenderer extends Renderer implements RendererInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var MatcherInterface
|
||||||
|
*/
|
||||||
|
protected $matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
|
protected $defaultOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $defaultOptions
|
||||||
|
*/
|
||||||
|
public function __construct(MatcherInterface $matcher, array $defaultOptions = [], ?string $charset = null)
|
||||||
|
{
|
||||||
|
$this->matcher = $matcher;
|
||||||
|
$this->defaultOptions = \array_merge([
|
||||||
|
'depth' => null,
|
||||||
|
'matchingDepth' => null,
|
||||||
|
'currentAsLink' => true,
|
||||||
|
'currentClass' => 'current',
|
||||||
|
'ancestorClass' => 'current_ancestor',
|
||||||
|
'firstClass' => 'first',
|
||||||
|
'lastClass' => 'last',
|
||||||
|
'compressed' => false,
|
||||||
|
'allow_safe_labels' => false,
|
||||||
|
'clear_matcher' => true,
|
||||||
|
'leaf_class' => null,
|
||||||
|
'branch_class' => null,
|
||||||
|
], $defaultOptions);
|
||||||
|
|
||||||
|
parent::__construct($charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(ItemInterface $item, array $options = []): string
|
||||||
|
{
|
||||||
|
$options = \array_merge($this->defaultOptions, $options);
|
||||||
|
|
||||||
|
$html = $this->renderList($item, $item->getChildrenAttributes(), $options);
|
||||||
|
|
||||||
|
if ($options['clear_matcher']) {
|
||||||
|
$this->matcher->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string|bool|null> $attributes
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
protected function renderList(ItemInterface $item, array $attributes, array $options): string
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Return an empty string if any of the following are true:
|
||||||
|
* a) The menu has no children eligible to be displayed
|
||||||
|
* b) The depth is 0
|
||||||
|
* c) This menu item has been explicitly set to hide its children
|
||||||
|
*/
|
||||||
|
if (0 === $options['depth'] || !$item->hasChildren() || !$item->getDisplayChildren()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = $this->format('<ul'.$this->renderHtmlAttributes($attributes).'>', 'ul', $item->getLevel(), $options);
|
||||||
|
$html .= $this->renderChildren($item, $options);
|
||||||
|
$html .= $this->format('</ul>', 'ul', $item->getLevel(), $options);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders all of the children of this menu.
|
||||||
|
*
|
||||||
|
* This calls ->renderItem() on each menu item, which instructs each
|
||||||
|
* menu item to render themselves as an <li> tag (with nested ul if it
|
||||||
|
* has children).
|
||||||
|
* This method updates the depth for the children.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options the options to render the item
|
||||||
|
*/
|
||||||
|
protected function renderChildren(ItemInterface $item, array $options): string
|
||||||
|
{
|
||||||
|
// render children with a depth - 1
|
||||||
|
if (null !== $options['depth']) {
|
||||||
|
--$options['depth'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $options['matchingDepth'] && $options['matchingDepth'] > 0) {
|
||||||
|
--$options['matchingDepth'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = '';
|
||||||
|
foreach ($item->getChildren() as $child) {
|
||||||
|
$html .= $this->renderItem($child, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the parent menu item to render this menu.
|
||||||
|
*
|
||||||
|
* This renders the li tag to fit into the parent ul as well as its
|
||||||
|
* own nested ul tag if this menu item has children
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options The options to render the item
|
||||||
|
*/
|
||||||
|
protected function renderItem(ItemInterface $item, array $options): string
|
||||||
|
{
|
||||||
|
// if we don't have access or this item is marked to not be shown
|
||||||
|
if (!$item->isDisplayed()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an array than can be imploded as a class list
|
||||||
|
$class = (array) $item->getAttribute('class');
|
||||||
|
|
||||||
|
if ($this->matcher->isCurrent($item)) {
|
||||||
|
$class[] = $options['currentClass'];
|
||||||
|
} elseif ($this->matcher->isAncestor($item, $options['matchingDepth'])) {
|
||||||
|
$class[] = $options['ancestorClass'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item->actsLikeFirst()) {
|
||||||
|
$class[] = $options['firstClass'];
|
||||||
|
}
|
||||||
|
if ($item->actsLikeLast()) {
|
||||||
|
$class[] = $options['lastClass'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 !== $options['depth'] && $item->hasChildren()) {
|
||||||
|
if (null !== $options['branch_class'] && $item->getDisplayChildren()) {
|
||||||
|
$class[] = $options['branch_class'];
|
||||||
|
}
|
||||||
|
} elseif (null !== $options['leaf_class']) {
|
||||||
|
$class[] = $options['leaf_class'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve the attributes and put the final class string back on it
|
||||||
|
$attributes = $item->getAttributes();
|
||||||
|
if (!empty($class)) {
|
||||||
|
$attributes['class'] = \implode(' ', $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// opening li tag
|
||||||
|
$html = $this->format('<li'.$this->renderHtmlAttributes($attributes).'>', 'li', $item->getLevel(), $options);
|
||||||
|
|
||||||
|
// render the text/link inside the li tag
|
||||||
|
//$html .= $this->format($item->getUri() ? $item->renderLink() : $item->renderLabel(), 'link', $item->getLevel());
|
||||||
|
$html .= $this->renderLink($item, $options);
|
||||||
|
|
||||||
|
// renders the embedded ul
|
||||||
|
$childrenClass = (array) $item->getChildrenAttribute('class');
|
||||||
|
$childrenClass[] = 'menu_level_'.$item->getLevel();
|
||||||
|
|
||||||
|
$childrenAttributes = $item->getChildrenAttributes();
|
||||||
|
$childrenAttributes['class'] = \implode(' ', $childrenClass);
|
||||||
|
|
||||||
|
$html .= $this->renderList($item, $childrenAttributes, $options);
|
||||||
|
|
||||||
|
// closing li tag
|
||||||
|
$html .= $this->format('</li>', 'li', $item->getLevel(), $options);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the link in a a tag with link attributes or
|
||||||
|
* the label in a span tag with label attributes
|
||||||
|
*
|
||||||
|
* Tests if item has a an uri and if not tests if it's
|
||||||
|
* the current item and if the text has to be rendered
|
||||||
|
* as a link or not.
|
||||||
|
*
|
||||||
|
* @param ItemInterface $item The item to render the link or label for
|
||||||
|
* @param array<string, mixed> $options The options to render the item
|
||||||
|
*/
|
||||||
|
protected function renderLink(ItemInterface $item, array $options = []): string
|
||||||
|
{
|
||||||
|
if (null !== $item->getUri() && (!$this->matcher->isCurrent($item) || $options['currentAsLink'])) {
|
||||||
|
$text = $this->renderLinkElement($item, $options);
|
||||||
|
} else {
|
||||||
|
$text = $this->renderSpanElement($item, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->format($text, 'link', $item->getLevel(), $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
protected function renderLinkElement(ItemInterface $item, array $options): string
|
||||||
|
{
|
||||||
|
\assert(null !== $item->getUri());
|
||||||
|
|
||||||
|
return \sprintf('<a href="%s"%s>%s</a>', $this->escape($item->getUri()), $this->renderHtmlAttributes($item->getLinkAttributes()), $this->renderLabel($item, $options));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
protected function renderSpanElement(ItemInterface $item, array $options): string
|
||||||
|
{
|
||||||
|
return \sprintf('<span%s>%s</span>', $this->renderHtmlAttributes($item->getLabelAttributes()), $this->renderLabel($item, $options));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
protected function renderLabel(ItemInterface $item, array $options): string
|
||||||
|
{
|
||||||
|
if ($options['allow_safe_labels'] && $item->getExtra('safe_label', false)) {
|
||||||
|
return $item->getLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->escape($item->getLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If $this->renderCompressed is on, this will apply the necessary
|
||||||
|
* spacing and line-breaking so that the particular thing being rendered
|
||||||
|
* makes up its part in a fully-rendered and spaced menu.
|
||||||
|
*
|
||||||
|
* @param string $html The html to render in an (un)formatted way
|
||||||
|
* @param string $type The type [ul,link,li] of thing being rendered
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
protected function format(string $html, string $type, int $level, array $options): string
|
||||||
|
{
|
||||||
|
if ($options['compressed']) {
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
$spacing = 0;
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'ul':
|
||||||
|
case 'link':
|
||||||
|
$spacing = $level * 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'li':
|
||||||
|
$spacing = $level * 4 - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \str_repeat(' ', $spacing).$html."\n";
|
||||||
|
}
|
||||||
|
}
|
51
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/PsrProvider.php
vendored
Normal file
51
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/PsrProvider.php
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Renderer;
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A renderer provider getting the renderer from a PSR-11 container.
|
||||||
|
*
|
||||||
|
* This menu provider does not support using options, as it cannot pass them to the container
|
||||||
|
* to alter the menu building. Use a different provider in case you need support for options.
|
||||||
|
*/
|
||||||
|
class PsrProvider implements RendererProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $defaultRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $defaultRenderer id of the default renderer (it should exist in the container to avoid weird failures)
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $container, string $defaultRenderer)
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
$this->defaultRenderer = $defaultRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(?string $name = null): RendererInterface
|
||||||
|
{
|
||||||
|
if (null === $name) {
|
||||||
|
$name = $this->defaultRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->container->has($name)) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('The renderer "%s" is not defined.', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->container->get($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function has(string $name): bool
|
||||||
|
{
|
||||||
|
return $this->container->has($name);
|
||||||
|
}
|
||||||
|
}
|
100
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/Renderer.php
vendored
Normal file
100
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/Renderer.php
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Renderer;
|
||||||
|
|
||||||
|
abstract class Renderer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $charset = 'UTF-8';
|
||||||
|
|
||||||
|
public function __construct(?string $charset = null)
|
||||||
|
{
|
||||||
|
if (null !== $charset) {
|
||||||
|
$this->charset = $charset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a HTML attribute
|
||||||
|
*
|
||||||
|
* @param string|bool $value
|
||||||
|
*/
|
||||||
|
protected function renderHtmlAttribute(string $name, $value): string
|
||||||
|
{
|
||||||
|
if (true === $value) {
|
||||||
|
return \sprintf('%s="%s"', $name, $this->escape($name));
|
||||||
|
}
|
||||||
|
if (false === $value) {
|
||||||
|
throw new \InvalidArgumentException('Value cannot be false.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return \sprintf('%s="%s"', $name, $this->escape($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders HTML attributes
|
||||||
|
*
|
||||||
|
* @param array<string, string|bool|null> $attributes
|
||||||
|
*/
|
||||||
|
protected function renderHtmlAttributes(array $attributes): string
|
||||||
|
{
|
||||||
|
return \implode('', \array_map([$this, 'htmlAttributesCallback'], \array_keys($attributes), \array_values($attributes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares an attribute key and value for HTML representation.
|
||||||
|
*
|
||||||
|
* It removes empty attributes.
|
||||||
|
*
|
||||||
|
* @param string $name The attribute name
|
||||||
|
* @param string|bool|null $value The attribute value
|
||||||
|
*
|
||||||
|
* @return string the HTML representation of the HTML key attribute pair
|
||||||
|
*/
|
||||||
|
private function htmlAttributesCallback(string $name, $value): string
|
||||||
|
{
|
||||||
|
if (false === $value || null === $value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ' '.$this->renderHtmlAttribute($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes an HTML value
|
||||||
|
*/
|
||||||
|
protected function escape(string $value): string
|
||||||
|
{
|
||||||
|
return $this->fixDoubleEscape(\htmlspecialchars($value, \ENT_QUOTES | \ENT_SUBSTITUTE, $this->charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes double escaped strings.
|
||||||
|
*
|
||||||
|
* @param string $escaped string to fix
|
||||||
|
*
|
||||||
|
* @return string A single escaped string
|
||||||
|
*/
|
||||||
|
protected function fixDoubleEscape(string $escaped): string
|
||||||
|
{
|
||||||
|
return (string) \preg_replace('/&([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', $escaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the HTML charset
|
||||||
|
*/
|
||||||
|
public function getCharset(): string
|
||||||
|
{
|
||||||
|
return $this->charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HTML charset
|
||||||
|
*/
|
||||||
|
public function setCharset(string $charset): void
|
||||||
|
{
|
||||||
|
$this->charset = $charset;
|
||||||
|
}
|
||||||
|
}
|
27
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/RendererInterface.php
vendored
Normal file
27
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/RendererInterface.php
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Renderer;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
interface RendererInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Renders menu tree.
|
||||||
|
*
|
||||||
|
* Common options:
|
||||||
|
* - depth: The depth at which the item is rendered
|
||||||
|
* null: no limit
|
||||||
|
* 0: no children
|
||||||
|
* 1: only direct children
|
||||||
|
* - currentAsLink: whether the current item should be a link
|
||||||
|
* - currentClass: class added to the current item
|
||||||
|
* - ancestorClass: class added to the ancestors of the current item
|
||||||
|
* - firstClass: class added to the first child
|
||||||
|
* - lastClass: class added to the last child
|
||||||
|
*
|
||||||
|
* @param ItemInterface $item Menu item
|
||||||
|
* @param array<string, mixed> $options some rendering options
|
||||||
|
*/
|
||||||
|
public function render(ItemInterface $item, array $options = []): string;
|
||||||
|
}
|
20
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/RendererProviderInterface.php
vendored
Normal file
20
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/RendererProviderInterface.php
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Renderer;
|
||||||
|
|
||||||
|
interface RendererProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieves a renderer by its name
|
||||||
|
*
|
||||||
|
* If null is given, a renderer marked as default is returned.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if the renderer does not exists
|
||||||
|
*/
|
||||||
|
public function get(?string $name = null): RendererInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a renderer exists
|
||||||
|
*/
|
||||||
|
public function has(string $name): bool;
|
||||||
|
}
|
66
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/TwigRenderer.php
vendored
Normal file
66
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Renderer/TwigRenderer.php
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Renderer;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\Matcher\MatcherInterface;
|
||||||
|
use Twig\Environment;
|
||||||
|
|
||||||
|
class TwigRenderer implements RendererInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Environment
|
||||||
|
*/
|
||||||
|
private $environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MatcherInterface
|
||||||
|
*/
|
||||||
|
private $matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
|
private $defaultOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $defaultOptions
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
Environment $environment,
|
||||||
|
string $template,
|
||||||
|
MatcherInterface $matcher,
|
||||||
|
array $defaultOptions = []
|
||||||
|
) {
|
||||||
|
$this->environment = $environment;
|
||||||
|
$this->matcher = $matcher;
|
||||||
|
$this->defaultOptions = \array_merge([
|
||||||
|
'depth' => null,
|
||||||
|
'matchingDepth' => null,
|
||||||
|
'currentAsLink' => true,
|
||||||
|
'currentClass' => 'current',
|
||||||
|
'ancestorClass' => 'current_ancestor',
|
||||||
|
'firstClass' => 'first',
|
||||||
|
'lastClass' => 'last',
|
||||||
|
'template' => $template,
|
||||||
|
'compressed' => false,
|
||||||
|
'allow_safe_labels' => false,
|
||||||
|
'clear_matcher' => true,
|
||||||
|
'leaf_class' => null,
|
||||||
|
'branch_class' => null,
|
||||||
|
], $defaultOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(ItemInterface $item, array $options = []): string
|
||||||
|
{
|
||||||
|
$options = \array_merge($this->defaultOptions, $options);
|
||||||
|
|
||||||
|
$html = $this->environment->render($options['template'], ['item' => $item, 'options' => $options, 'matcher' => $this->matcher]);
|
||||||
|
|
||||||
|
if ($options['clear_matcher']) {
|
||||||
|
$this->matcher->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
101
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Resources/views/knp_menu.html.twig
vendored
Normal file
101
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Resources/views/knp_menu.html.twig
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
{% extends 'knp_menu_base.html.twig' %}
|
||||||
|
|
||||||
|
{% macro attributes(attributes) %}
|
||||||
|
{% for name, value in attributes %}
|
||||||
|
{%- if value is not none and value is not same as(false) -%}
|
||||||
|
{{- ' %s="%s"'|format(name, value is same as(true) ? name|e : value|e)|raw -}}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% block compressed_root %}
|
||||||
|
{% apply spaceless %}
|
||||||
|
{{ block('root') }}
|
||||||
|
{% endapply %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block root %}
|
||||||
|
{% set listAttributes = item.childrenAttributes %}
|
||||||
|
{{ block('list') -}}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block list %}
|
||||||
|
{% if item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
|
||||||
|
{% import _self as knp_menu %}
|
||||||
|
<ul{{ knp_menu.attributes(listAttributes) }}>
|
||||||
|
{{ block('children') }}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block children %}
|
||||||
|
{# save current variables #}
|
||||||
|
{% set currentOptions = options %}
|
||||||
|
{% set currentItem = item %}
|
||||||
|
{# update the depth for children #}
|
||||||
|
{% if options.depth is not none %}
|
||||||
|
{% set options = options|merge({'depth': currentOptions.depth - 1}) %}
|
||||||
|
{% endif %}
|
||||||
|
{# update the matchingDepth for children #}
|
||||||
|
{% if options.matchingDepth is not none and options.matchingDepth > 0 %}
|
||||||
|
{% set options = options|merge({'matchingDepth': currentOptions.matchingDepth - 1}) %}
|
||||||
|
{% endif %}
|
||||||
|
{% for item in currentItem.children %}
|
||||||
|
{{ block('item') }}
|
||||||
|
{% endfor %}
|
||||||
|
{# restore current variables #}
|
||||||
|
{% set item = currentItem %}
|
||||||
|
{% set options = currentOptions %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block item %}
|
||||||
|
{% if item.displayed %}
|
||||||
|
{# building the class of the item #}
|
||||||
|
{%- set classes = item.attribute('class') is not empty ? [item.attribute('class')] : [] %}
|
||||||
|
{%- if matcher.isCurrent(item) %}
|
||||||
|
{%- set classes = classes|merge([options.currentClass]) %}
|
||||||
|
{%- elseif matcher.isAncestor(item, options.matchingDepth) %}
|
||||||
|
{%- set classes = classes|merge([options.ancestorClass]) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if item.actsLikeFirst %}
|
||||||
|
{%- set classes = classes|merge([options.firstClass]) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if item.actsLikeLast %}
|
||||||
|
{%- set classes = classes|merge([options.lastClass]) %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{# Mark item as "leaf" (no children) or as "branch" (has children that are displayed) #}
|
||||||
|
{% if item.hasChildren and options.depth is not same as(0) %}
|
||||||
|
{% if options.branch_class is not empty and item.displayChildren %}
|
||||||
|
{%- set classes = classes|merge([options.branch_class]) %}
|
||||||
|
{% endif %}
|
||||||
|
{% elseif options.leaf_class is not empty %}
|
||||||
|
{%- set classes = classes|merge([options.leaf_class]) %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- set attributes = item.attributes %}
|
||||||
|
{%- if classes is not empty %}
|
||||||
|
{%- set attributes = attributes|merge({'class': classes|join(' ')}) %}
|
||||||
|
{%- endif %}
|
||||||
|
{# displaying the item #}
|
||||||
|
{% import _self as knp_menu %}
|
||||||
|
<li{{ knp_menu.attributes(attributes) }}>
|
||||||
|
{%- if item.uri is not empty and (not matcher.isCurrent(item) or options.currentAsLink) %}
|
||||||
|
{{ block('linkElement') }}
|
||||||
|
{%- else %}
|
||||||
|
{{ block('spanElement') }}
|
||||||
|
{%- endif %}
|
||||||
|
{# render the list of children#}
|
||||||
|
{%- set childrenClasses = item.childrenAttribute('class') is not empty ? [item.childrenAttribute('class')] : [] %}
|
||||||
|
{%- set childrenClasses = childrenClasses|merge(['menu_level_' ~ item.level]) %}
|
||||||
|
{%- set listAttributes = item.childrenAttributes|merge({'class': childrenClasses|join(' ') }) %}
|
||||||
|
{{ block('list') }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block linkElement %}{% import _self as knp_menu %}<a href="{{ item.uri }}"{{ knp_menu.attributes(item.linkAttributes) }}>{{ block('label') }}</a>{% endblock %}
|
||||||
|
|
||||||
|
{% block spanElement %}{% import _self as knp_menu %}<span{{ knp_menu.attributes(item.labelAttributes) }}>{{ block('label') }}</span>{% endblock %}
|
||||||
|
|
||||||
|
{% block label %}{% if options.allow_safe_labels and item.getExtra('safe_label', false) %}{{ item.label|raw }}{% else %}{{ item.label }}{% endif %}{% endblock %}
|
@ -0,0 +1 @@
|
|||||||
|
{% if options.compressed %}{{ block('compressed_root') }}{% else %}{{ block('root') }}{% endif %}
|
11
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Resources/views/knp_menu_ordered.html.twig
vendored
Normal file
11
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Resources/views/knp_menu_ordered.html.twig
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'knp_menu.html.twig' %}
|
||||||
|
|
||||||
|
{% block list %}
|
||||||
|
{% import 'knp_menu.html.twig' as macros %}
|
||||||
|
|
||||||
|
{% if item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
|
||||||
|
<ol{{ macros.attributes(listAttributes) }}>
|
||||||
|
{{ block('children') }}
|
||||||
|
</ol>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
192
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php
vendored
Normal file
192
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php
vendored
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Twig;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\Matcher\MatcherInterface;
|
||||||
|
use Knp\Menu\Provider\MenuProviderInterface;
|
||||||
|
use Knp\Menu\Renderer\RendererProviderInterface;
|
||||||
|
use Knp\Menu\Util\MenuManipulator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class containing logic to retrieve and render menus from templating engines
|
||||||
|
*/
|
||||||
|
class Helper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var RendererProviderInterface
|
||||||
|
*/
|
||||||
|
private $rendererProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuProviderInterface|null
|
||||||
|
*/
|
||||||
|
private $menuProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuManipulator|null
|
||||||
|
*/
|
||||||
|
private $menuManipulator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MatcherInterface|null
|
||||||
|
*/
|
||||||
|
private $matcher;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererProviderInterface $rendererProvider,
|
||||||
|
?MenuProviderInterface $menuProvider = null,
|
||||||
|
?MenuManipulator $menuManipulator = null,
|
||||||
|
?MatcherInterface $matcher = null
|
||||||
|
) {
|
||||||
|
$this->rendererProvider = $rendererProvider;
|
||||||
|
$this->menuProvider = $menuProvider;
|
||||||
|
$this->menuManipulator = $menuManipulator;
|
||||||
|
$this->matcher = $matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves item in the menu, eventually using the menu provider.
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string $menu
|
||||||
|
* @param array<int, string> $path
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*
|
||||||
|
* @throws \BadMethodCallException when there is no menu provider and the menu is given by name
|
||||||
|
* @throws \LogicException
|
||||||
|
* @throws \InvalidArgumentException when the path is invalid
|
||||||
|
*/
|
||||||
|
public function get($menu, array $path = [], array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
if (!$menu instanceof ItemInterface) {
|
||||||
|
if (null === $this->menuProvider) {
|
||||||
|
throw new \BadMethodCallException('A menu provider must be set to retrieve a menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
$menuName = $menu;
|
||||||
|
$menu = $this->menuProvider->get($menuName, $options);
|
||||||
|
|
||||||
|
if (!$menu instanceof ItemInterface) {
|
||||||
|
throw new \LogicException(\sprintf('The menu "%s" exists, but is not a valid menu item object. Check where you created the menu to be sure it returns an ItemInterface object.', $menuName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($path as $child) {
|
||||||
|
$menu = $menu->getChild($child);
|
||||||
|
if (null === $menu) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('The menu has no child named "%s"', $child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a menu with the specified renderer.
|
||||||
|
*
|
||||||
|
* If the argument is an array, it will follow the path in the tree to
|
||||||
|
* get the needed item. The first element of the array is the whole menu.
|
||||||
|
* If the menu is a string instead of an ItemInterface, the provider
|
||||||
|
* will be used.
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string|array<ItemInterface|string> $menu
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function render($menu, array $options = [], ?string $renderer = null): string
|
||||||
|
{
|
||||||
|
$menu = $this->castMenu($menu);
|
||||||
|
|
||||||
|
return $this->rendererProvider->get($renderer)->render($menu, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders an array ready to be used for breadcrumbs.
|
||||||
|
*
|
||||||
|
* Each element in the array will be an array with 3 keys:
|
||||||
|
* - `label` containing the label of the item
|
||||||
|
* - `url` containing the url of the item (may be `null`)
|
||||||
|
* - `item` containing the original item (may be `null` for the extra items)
|
||||||
|
*
|
||||||
|
* The subItem can be one of the following forms
|
||||||
|
* * 'subItem'
|
||||||
|
* * ItemInterface object
|
||||||
|
* * ['subItem' => '@homepage']
|
||||||
|
* * ['subItem1', 'subItem2']
|
||||||
|
* * [['label' => 'subItem1', 'url' => '@homepage'], ['label' => 'subItem2']]
|
||||||
|
*
|
||||||
|
* @param mixed $menu
|
||||||
|
* @param mixed $subItem A string or array to append onto the end of the array
|
||||||
|
* @phpstan-param string|ItemInterface|array<int|string, string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface>|\Traversable<string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface> $subItem
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
* @phpstan-return list<array{label: string, uri: string|null, item: ItemInterface|null}>
|
||||||
|
*/
|
||||||
|
public function getBreadcrumbsArray($menu, $subItem = null): array
|
||||||
|
{
|
||||||
|
if (null === $this->menuManipulator) {
|
||||||
|
throw new \BadMethodCallException('The menu manipulator must be set to get the breadcrumbs array');
|
||||||
|
}
|
||||||
|
|
||||||
|
$menu = $this->castMenu($menu);
|
||||||
|
|
||||||
|
return $this->menuManipulator->getBreadcrumbsArray($menu, $subItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current item of a menu.
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string|array<ItemInterface|string> $menu
|
||||||
|
*/
|
||||||
|
public function getCurrentItem($menu): ?ItemInterface
|
||||||
|
{
|
||||||
|
$menu = $this->castMenu($menu);
|
||||||
|
|
||||||
|
return $this->retrieveCurrentItem($menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ItemInterface|string|array<ItemInterface|string> $menu
|
||||||
|
*/
|
||||||
|
private function castMenu($menu): ItemInterface
|
||||||
|
{
|
||||||
|
if (!$menu instanceof ItemInterface) {
|
||||||
|
$path = [];
|
||||||
|
if (\is_array($menu)) {
|
||||||
|
if (empty($menu)) {
|
||||||
|
throw new \InvalidArgumentException('The array cannot be empty');
|
||||||
|
}
|
||||||
|
$path = $menu;
|
||||||
|
$menu = \array_shift($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->get($menu, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function retrieveCurrentItem(ItemInterface $item): ?ItemInterface
|
||||||
|
{
|
||||||
|
if (null === $this->matcher) {
|
||||||
|
throw new \BadMethodCallException('The matcher must be set to get the current item of a menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->matcher->isCurrent($item)) {
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->matcher->isAncestor($item)) {
|
||||||
|
foreach ($item->getChildren() as $child) {
|
||||||
|
$currentItem = $this->retrieveCurrentItem($child);
|
||||||
|
|
||||||
|
if (null !== $currentItem) {
|
||||||
|
return $currentItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
166
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Twig/MenuExtension.php
vendored
Normal file
166
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Twig/MenuExtension.php
vendored
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Twig;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
use Knp\Menu\Matcher\MatcherInterface;
|
||||||
|
use Knp\Menu\Util\MenuManipulator;
|
||||||
|
use Twig\Extension\AbstractExtension;
|
||||||
|
use Twig\TwigFilter;
|
||||||
|
use Twig\TwigFunction;
|
||||||
|
use Twig\TwigTest;
|
||||||
|
|
||||||
|
class MenuExtension extends AbstractExtension
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Helper
|
||||||
|
*/
|
||||||
|
private $helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MatcherInterface|null
|
||||||
|
*/
|
||||||
|
private $matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MenuManipulator|null
|
||||||
|
*/
|
||||||
|
private $menuManipulator;
|
||||||
|
|
||||||
|
public function __construct(Helper $helper, ?MatcherInterface $matcher = null, ?MenuManipulator $menuManipulator = null)
|
||||||
|
{
|
||||||
|
$this->helper = $helper;
|
||||||
|
$this->matcher = $matcher;
|
||||||
|
$this->menuManipulator = $menuManipulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, TwigFunction>
|
||||||
|
*/
|
||||||
|
public function getFunctions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFunction('knp_menu_get', [$this, 'get']),
|
||||||
|
new TwigFunction('knp_menu_render', [$this, 'render'], ['is_safe' => ['html']]),
|
||||||
|
new TwigFunction('knp_menu_get_breadcrumbs_array', [$this, 'getBreadcrumbsArray']),
|
||||||
|
new TwigFunction('knp_menu_get_current_item', [$this, 'getCurrentItem']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, TwigFilter>
|
||||||
|
*/
|
||||||
|
public function getFilters(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFilter('knp_menu_as_string', [$this, 'pathAsString']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, TwigTest>
|
||||||
|
*/
|
||||||
|
public function getTests(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigTest('knp_menu_current', [$this, 'isCurrent']),
|
||||||
|
new TwigTest('knp_menu_ancestor', [$this, 'isAncestor']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an item following a path in the tree.
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string $menu
|
||||||
|
* @param array<int, string> $path
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function get($menu, array $path = [], array $options = []): ItemInterface
|
||||||
|
{
|
||||||
|
return $this->helper->get($menu, $path, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a menu with the specified renderer.
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string|array<ItemInterface|string> $menu
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function render($menu, array $options = [], ?string $renderer = null): string
|
||||||
|
{
|
||||||
|
return $this->helper->render($menu, $options, $renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array ready to be used for breadcrumbs.
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string|array<ItemInterface|string> $menu
|
||||||
|
* @param string|array<string|null>|null $subItem
|
||||||
|
* @phpstan-param string|ItemInterface|array<int|string, string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface>|\Traversable<string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface> $subItem
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
* @phpstan-return list<array{label: string, uri: string|null, item: ItemInterface|null}>
|
||||||
|
*/
|
||||||
|
public function getBreadcrumbsArray($menu, $subItem = null): array
|
||||||
|
{
|
||||||
|
return $this->helper->getBreadcrumbsArray($menu, $subItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current item of a menu.
|
||||||
|
*
|
||||||
|
* @param ItemInterface|string $menu
|
||||||
|
*/
|
||||||
|
public function getCurrentItem($menu): ItemInterface
|
||||||
|
{
|
||||||
|
$rootItem = $this->get($menu);
|
||||||
|
|
||||||
|
$currentItem = $this->helper->getCurrentItem($rootItem);
|
||||||
|
|
||||||
|
if (null === $currentItem) {
|
||||||
|
$currentItem = $rootItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $currentItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representation of this menu item
|
||||||
|
*
|
||||||
|
* e.g. Top Level > Second Level > This menu
|
||||||
|
*/
|
||||||
|
public function pathAsString(ItemInterface $menu, string $separator = ' > '): string
|
||||||
|
{
|
||||||
|
if (null === $this->menuManipulator) {
|
||||||
|
throw new \BadMethodCallException('The menu manipulator must be set to get the breadcrumbs array');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->menuManipulator->getPathAsString($menu, $separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an item is current.
|
||||||
|
*/
|
||||||
|
public function isCurrent(ItemInterface $item): bool
|
||||||
|
{
|
||||||
|
if (null === $this->matcher) {
|
||||||
|
throw new \BadMethodCallException('The matcher must be set to get the breadcrumbs array');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->matcher->isCurrent($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an item is the ancestor of a current item.
|
||||||
|
*
|
||||||
|
* @param int|null $depth The max depth to look for the item
|
||||||
|
*/
|
||||||
|
public function isAncestor(ItemInterface $item, ?int $depth = null): bool
|
||||||
|
{
|
||||||
|
if (null === $this->matcher) {
|
||||||
|
throw new \BadMethodCallException('The matcher must be set to get the breadcrumbs array');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->matcher->isAncestor($item, $depth);
|
||||||
|
}
|
||||||
|
}
|
304
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Util/MenuManipulator.php
vendored
Normal file
304
plugins/page-toc/vendor/knplabs/knp-menu/src/Knp/Menu/Util/MenuManipulator.php
vendored
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Knp\Menu\Util;
|
||||||
|
|
||||||
|
use Knp\Menu\ItemInterface;
|
||||||
|
|
||||||
|
class MenuManipulator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Moves item to specified position. Rearrange siblings accordingly.
|
||||||
|
*
|
||||||
|
* @param int $position position to move child to
|
||||||
|
*/
|
||||||
|
public function moveToPosition(ItemInterface $item, int $position): void
|
||||||
|
{
|
||||||
|
if (null !== $parent = $item->getParent()) {
|
||||||
|
$this->moveChildToPosition($parent, $item, $position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves child to specified position. Rearrange other children accordingly.
|
||||||
|
*
|
||||||
|
* @param ItemInterface $child Child to move
|
||||||
|
* @param int $position Position to move child to
|
||||||
|
*/
|
||||||
|
public function moveChildToPosition(ItemInterface $item, ItemInterface $child, int $position): void
|
||||||
|
{
|
||||||
|
$name = $child->getName();
|
||||||
|
$order = \array_keys($item->getChildren());
|
||||||
|
|
||||||
|
$oldPosition = \array_search($name, $order);
|
||||||
|
unset($order[$oldPosition]);
|
||||||
|
|
||||||
|
$order = \array_values($order);
|
||||||
|
|
||||||
|
\array_splice($order, $position, 0, $name);
|
||||||
|
$item->reorderChildren($order);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves item to first position. Rearrange siblings accordingly.
|
||||||
|
*/
|
||||||
|
public function moveToFirstPosition(ItemInterface $item): void
|
||||||
|
{
|
||||||
|
$this->moveToPosition($item, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves item to last position. Rearrange siblings accordingly.
|
||||||
|
*/
|
||||||
|
public function moveToLastPosition(ItemInterface $item): void
|
||||||
|
{
|
||||||
|
if (null !== $parent = $item->getParent()) {
|
||||||
|
$this->moveToPosition($item, $parent->count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get slice of menu as another menu.
|
||||||
|
*
|
||||||
|
* If offset and/or length are numeric, it works like in array_slice function:
|
||||||
|
*
|
||||||
|
* If offset is non-negative, slice will start at the offset.
|
||||||
|
* If offset is negative, slice will start that far from the end.
|
||||||
|
*
|
||||||
|
* If length is null, slice will have all elements.
|
||||||
|
* If length is positive, slice will have that many elements.
|
||||||
|
* If length is negative, slice will stop that far from the end.
|
||||||
|
*
|
||||||
|
* It's possible to mix names/object/numeric, for example:
|
||||||
|
* slice("child1", 2);
|
||||||
|
* slice(3, $child5);
|
||||||
|
* Note: when using a child as limit, it will not be included in the returned menu.
|
||||||
|
* the slice is done before this menu.
|
||||||
|
*
|
||||||
|
* @param mixed $offset name of child, child object, or numeric offset
|
||||||
|
* @param string|int|ItemInterface $length name of child, child object, or numeric length
|
||||||
|
*/
|
||||||
|
public function slice(ItemInterface $item, $offset, $length = null): ItemInterface
|
||||||
|
{
|
||||||
|
$names = \array_keys($item->getChildren());
|
||||||
|
if ($offset instanceof ItemInterface) {
|
||||||
|
$offset = $offset->getName();
|
||||||
|
}
|
||||||
|
if (!\is_int($offset)) {
|
||||||
|
$offset = \array_search($offset, $names, true);
|
||||||
|
if (false === $offset) {
|
||||||
|
throw new \InvalidArgumentException('Not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $length) {
|
||||||
|
if ($length instanceof ItemInterface) {
|
||||||
|
$length = $length->getName();
|
||||||
|
}
|
||||||
|
if (!\is_int($length)) {
|
||||||
|
$index = \array_search($length, $names, true);
|
||||||
|
$length = ($index < $offset) ? 0 : $index - $offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$slicedItem = $item->copy();
|
||||||
|
$children = \array_slice($slicedItem->getChildren(), $offset, $length);
|
||||||
|
$slicedItem->setChildren($children);
|
||||||
|
|
||||||
|
return $slicedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split menu into two distinct menus.
|
||||||
|
*
|
||||||
|
* @param string|int|ItemInterface $length name of child, child object, or numeric length
|
||||||
|
*
|
||||||
|
* @phpstan-return array{primary: ItemInterface, secondary: ItemInterface}
|
||||||
|
*
|
||||||
|
* @return array Array with two menus, with "primary" and "secondary" key
|
||||||
|
*/
|
||||||
|
public function split(ItemInterface $item, $length): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'primary' => $this->slice($item, 0, $length),
|
||||||
|
'secondary' => $this->slice($item, $length),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls a method recursively on all of the children of this item
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* $menu->callRecursively('setShowChildren', [false]);
|
||||||
|
*
|
||||||
|
* @param array<int|string, mixed> $arguments
|
||||||
|
*/
|
||||||
|
public function callRecursively(ItemInterface $item, string $method, array $arguments = []): void
|
||||||
|
{
|
||||||
|
$item->$method(...$arguments);
|
||||||
|
|
||||||
|
foreach ($item->getChildren() as $child) {
|
||||||
|
$this->callRecursively($child, $method, $arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string representation of this menu item
|
||||||
|
*
|
||||||
|
* e.g. Top Level > Second Level > This menu
|
||||||
|
*/
|
||||||
|
public function getPathAsString(ItemInterface $item, string $separator = ' > '): string
|
||||||
|
{
|
||||||
|
$children = [];
|
||||||
|
$obj = $item;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$children[] = $obj->getLabel();
|
||||||
|
} while ($obj = $obj->getParent());
|
||||||
|
|
||||||
|
return \implode($separator, \array_reverse($children));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|null $depth the depth until which children should be exported (null means unlimited)
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(ItemInterface $item, ?int $depth = null): array
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'name' => $item->getName(),
|
||||||
|
'label' => $item->getLabel(),
|
||||||
|
'uri' => $item->getUri(),
|
||||||
|
'attributes' => $item->getAttributes(),
|
||||||
|
'labelAttributes' => $item->getLabelAttributes(),
|
||||||
|
'linkAttributes' => $item->getLinkAttributes(),
|
||||||
|
'childrenAttributes' => $item->getChildrenAttributes(),
|
||||||
|
'extras' => $item->getExtras(),
|
||||||
|
'display' => $item->isDisplayed(),
|
||||||
|
'displayChildren' => $item->getDisplayChildren(),
|
||||||
|
'current' => $item->isCurrent(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// export the children as well, unless explicitly disabled
|
||||||
|
if (0 !== $depth) {
|
||||||
|
$childDepth = null === $depth ? null : $depth - 1;
|
||||||
|
$array['children'] = [];
|
||||||
|
foreach ($item->getChildren() as $key => $child) {
|
||||||
|
$array['children'][$key] = $this->toArray($child, $childDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders an array ready to be used for breadcrumbs.
|
||||||
|
*
|
||||||
|
* Each element in the array will be an array with 3 keys:
|
||||||
|
* - `label` containing the label of the item
|
||||||
|
* - `url` containing the url of the item (may be `null`)
|
||||||
|
* - `item` containing the original item (may be `null` for the extra items)
|
||||||
|
*
|
||||||
|
* The subItem can be one of the following forms
|
||||||
|
* * 'subItem'
|
||||||
|
* * ItemInterface object
|
||||||
|
* * ['subItem' => '@homepage']
|
||||||
|
* * ['subItem1', 'subItem2']
|
||||||
|
* * [['label' => 'subItem1', 'url' => '@homepage'], ['label' => 'subItem2']]
|
||||||
|
*
|
||||||
|
* @param string|ItemInterface|array<int|string, mixed>|\Traversable<mixed> $subItem A string or array to append onto the end of the array
|
||||||
|
* @phpstan-param string|ItemInterface|array<int|string, string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface>|\Traversable<string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface> $subItem
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
* @phpstan-return list<array{label: string, uri: string|null, item: ItemInterface|null}>
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if an element of the subItem is invalid
|
||||||
|
*/
|
||||||
|
public function getBreadcrumbsArray(ItemInterface $item, $subItem = null): array
|
||||||
|
{
|
||||||
|
$breadcrumbs = $this->buildBreadcrumbsArray($item);
|
||||||
|
|
||||||
|
if (null === $subItem) {
|
||||||
|
return $breadcrumbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($subItem instanceof ItemInterface) {
|
||||||
|
$breadcrumbs[] = $this->getBreadcrumbsItem($subItem);
|
||||||
|
|
||||||
|
return $breadcrumbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_array($subItem) && !$subItem instanceof \Traversable) {
|
||||||
|
$subItem = [$subItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($subItem as $key => $value) {
|
||||||
|
switch (true) {
|
||||||
|
case $value instanceof ItemInterface:
|
||||||
|
$value = $this->getBreadcrumbsItem($value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case \is_array($value):
|
||||||
|
// Assume we already have the appropriate array format for the element
|
||||||
|
break;
|
||||||
|
|
||||||
|
case \is_int($key) && \is_string($value):
|
||||||
|
$value = [
|
||||||
|
'label' => (string) $value,
|
||||||
|
'uri' => null,
|
||||||
|
'item' => null,
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case \is_scalar($value):
|
||||||
|
$value = [
|
||||||
|
'label' => (string) $key,
|
||||||
|
'uri' => (string) $value,
|
||||||
|
'item' => null,
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case null === $value:
|
||||||
|
$value = [
|
||||||
|
'label' => (string) $key,
|
||||||
|
'uri' => null,
|
||||||
|
'item' => null,
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \InvalidArgumentException(\sprintf('Invalid value supplied for the key "%s". It should be an item, an array or a scalar', $key));
|
||||||
|
}
|
||||||
|
|
||||||
|
$breadcrumbs[] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $breadcrumbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-return list<array{label: string, uri: string|null, item: ItemInterface|null}>
|
||||||
|
*/
|
||||||
|
private function buildBreadcrumbsArray(ItemInterface $item): array
|
||||||
|
{
|
||||||
|
$breadcrumb = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
$breadcrumb[] = $this->getBreadcrumbsItem($item);
|
||||||
|
} while ($item = $item->getParent());
|
||||||
|
|
||||||
|
return \array_reverse($breadcrumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-return array{label: string, uri: string|null, item: ItemInterface}
|
||||||
|
*/
|
||||||
|
private function getBreadcrumbsItem(ItemInterface $item): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'label' => $item->getLabel(),
|
||||||
|
'uri' => $item->getUri(),
|
||||||
|
'item' => $item,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user