Passed
Push — master ( ea4658...dc61d8 )
by Josh
02:44
created

Configurator::addHeaderId()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 25
ccs 15
cts 15
cp 1
rs 9.7998
c 0
b 0
f 0
cc 4
nc 8
nop 2
crap 4
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2020 The s9e authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Plugins\Litedown;
9
10
use s9e\TextFormatter\Configurator\Items\Tag;
11
use s9e\TextFormatter\Plugins\ConfiguratorBase;
12
use s9e\TextFormatter\Plugins\Litedown\Parser\Slugger;
13
14
class Configurator extends ConfiguratorBase
15
{
16
	/**
17
	* @var bool Whether to decode HTML entities in attribute values
18
	*/
19
	public $decodeHtmlEntities = false;
20
21
	/**
22
	* @var array Default tags
23
	*/
24
	protected $tags = [
25
		'C'      => '<code><xsl:apply-templates/></code>',
26
		'CODE'   => [
27
			'attributes' => [
28
				'lang' => [
29
					'filterChain' => ['#simpletext'],
30
					'required'    => false
31
				]
32
			],
33
			'template' =>
34
				'<pre>
35
					<code>
36
						<xsl:if test="@lang">
37
							<xsl:attribute name="class">
38
								<xsl:text>language-</xsl:text>
39
								<xsl:value-of select="@lang"/>
40
							</xsl:attribute>
41
						</xsl:if>
42
						<xsl:apply-templates/>
43
					</code>
44
				</pre>'
45
		],
46
		'DEL'    => '<del><xsl:apply-templates/></del>',
47
		'EM'     => '<em><xsl:apply-templates/></em>',
48
		'EMAIL'  => [
49
			'attributes' => ['email' => ['filterChain' => ['#email']]],
50
			'template'   => '<a href="mailto:{@email}"><xsl:apply-templates/></a>'
51
		],
52
		'H1'     => '<h1><xsl:apply-templates/></h1>',
53
		'H2'     => '<h2><xsl:apply-templates/></h2>',
54
		'H3'     => '<h3><xsl:apply-templates/></h3>',
55
		'H4'     => '<h4><xsl:apply-templates/></h4>',
56
		'H5'     => '<h5><xsl:apply-templates/></h5>',
57
		'H6'     => '<h6><xsl:apply-templates/></h6>',
58
		'HR'     => '<hr/>',
59
		'IMG'    => [
60
			'attributes' => [
61
				'alt'   => ['required'    => false   ],
62
				'src'   => ['filterChain' => ['#url']],
63
				'title' => ['required'    => false   ]
64
			],
65
			'template' => '<img src="{@src}"><xsl:copy-of select="@alt"/><xsl:copy-of select="@title"/></img>'
66
		],
67
		'ISPOILER' => '<span class="spoiler" data-s9e-livepreview-ignore-attrs="style" onclick="removeAttribute(\'style\')" style="background:#444;color:transparent"><xsl:apply-templates/></span>',
68
		'LI'     => '<li><xsl:apply-templates/></li>',
69
		'LIST'   => [
70
			'attributes' => [
71
				'start' => [
72
					'filterChain' => ['#uint'],
73
					'required'    => false
74
				],
75
				'type' => [
76
					'filterChain' => ['#simpletext'],
77
					'required'    => false
78
				]
79
			],
80
			'template' =>
81
				'<xsl:choose>
82
					<xsl:when test="not(@type)">
83
						<ul><xsl:apply-templates/></ul>
84
					</xsl:when>
85
					<xsl:otherwise>
86
						<ol><xsl:copy-of select="@start"/><xsl:apply-templates/></ol>
87
					</xsl:otherwise>
88
				</xsl:choose>'
89
		],
90
		'QUOTE'   => '<blockquote><xsl:apply-templates/></blockquote>',
91
		'SPOILER' => '<details class="spoiler" data-s9e-livepreview-ignore-attrs="open"><xsl:apply-templates/></details>',
92
		'STRONG'  => '<strong><xsl:apply-templates/></strong>',
93
		'SUB'     => '<sub><xsl:apply-templates/></sub>',
94
		'SUP'     => '<sup><xsl:apply-templates/></sup>',
95
		'URL'     => [
96
			'attributes' => [
97
				'title' => ['required'    => false   ],
98
				'url'   => ['filterChain' => ['#url']]
99
			],
100
			'template' => '<a href="{@url}"><xsl:copy-of select="@title"/><xsl:apply-templates/></a>'
101
		]
102
	];
103
104
	/**
105
	* {@inheritdoc}
106
	*/
107 12
	protected function setUp()
108
	{
109 12
		$this->configurator->rulesGenerator->append('ManageParagraphs');
110
111 12
		foreach ($this->tags as $tagName => $tagConfig)
112
		{
113
			// Skip this tag if it already exists
114 12
			if (isset($this->configurator->tags[$tagName]))
115
			{
116 1
				continue;
117
			}
118
119
			// If the tag's config is a single string, it's really its default template
120 12
			if (is_string($tagConfig))
121
			{
122 12
				$tagConfig = ['template' => $tagConfig];
123
			}
124
125
			// Add this tag
126 12
			$this->configurator->tags->add($tagName, $tagConfig);
127
		}
128
	}
129
130
	/**
131
	* Add an "id" attribute to headers
132
	*
133
	* @param  string $prefix Prefix used for the "id" value
134
	* @return void
135
	*/
136 4
	public function addHeadersId(string $prefix = ''): void
137
	{
138 4
		for ($i = 1; $i <= 6; ++$i)
139
		{
140 4
			$tagName = 'H' . $i;
141 4
			if (isset($this->configurator->tags[$tagName]))
142
			{
143 4
				$this->addHeaderId($this->configurator->tags[$tagName], $prefix);
144
			}
145
		}
146
	}
147
148
	/**
149
	* Add an "id" attribute to given tag
150
	*
151
	* @param  Tag    $tag
152
	* @param  string $prefix Prefix used for the "id" value
153
	* @return void
154
	*/
155 4
	protected function addHeaderId(Tag $tag, string $prefix): void
156
	{
157 4
		if (!isset($tag->attributes['slug']))
158
		{
159 4
			unset($tag->attributes['slug']);
160
		}
161
162 4
		$tag->attributes->add('slug')->required = false;
163 4
		$tag->filterChain
164 4
			->append(Slugger::class . '::setTagSlug($tag, $innerText)')
165 4
			->setJS(Slugger::getJS());
166
167 4
		$dom = $tag->template->asDOM();
1 ignored issue
show
introduced by
The property template is declared write-only in s9e\TextFormatter\Configurator\Items\Tag.
Loading history...
168 4
		foreach ($dom->query('//xsl:if[@test = "@slug"]') as $if)
169
		{
170
			// Remove any pre-existing xsl:if from previous invocations
171 1
			$if->remove();
172
		}
173 4
		foreach ($dom->query('//h1 | //h2 | //h3 | //h4 | //h5 | //h6') as $header)
174
		{
175 4
			$header->prependXslIf('@slug')
176 4
			       ->appendXslAttribute('id', $prefix)
177 4
			       ->appendXslValueOf('@slug');
178
		}
179 4
		$dom->saveChanges();
180
	}
181
182
	/**
183
	* {@inheritdoc}
184
	*/
185 2
	public function asConfig()
186
	{
187 2
		return ['decodeHtmlEntities' => (bool) $this->decodeHtmlEntities];
188
	}
189
190
	/**
191
	* {@inheritdoc}
192
	*/
193 2
	public function getJSHints()
194
	{
195 2
		return ['LITEDOWN_DECODE_HTML_ENTITIES' => (int) $this->decodeHtmlEntities];
196
	}
197
198
	/**
199
	* {@inheritdoc}
200
	*/
201 1
	public function getJSParser()
202
	{
203 1
		$js = file_get_contents(__DIR__ . '/Parser/ParsedText.js') . "\n"
204 1
		    . file_get_contents(__DIR__ . '/Parser/Passes/AbstractInlineMarkup.js') . "\n"
205 1
		    . file_get_contents(__DIR__ . '/Parser/Passes/AbstractScript.js') . "\n"
206 1
		    . file_get_contents(__DIR__ . '/Parser/LinkAttributesSetter.js');
207
208
		$passes = [
209 1
			'Blocks',
210
			'LinkReferences',
211
			'InlineCode',
212
			'Images',
213
			'InlineSpoiler',
214
			'Links',
215
			'Strikethrough',
216
			'Subscript',
217
			'Superscript',
218
			'Emphasis',
219
			'ForcedLineBreaks'
220
		];
221 1
		foreach ($passes as $pass)
222
		{
223
			$js .= "\n(function(){\n"
224 1
			     . file_get_contents(__DIR__ . '/Parser/Passes/' . $pass . '.js') . "\n"
225 1
			     . "parse();\n"
226 1
			     . "})();";
227
		}
228
229 1
		return $js;
230
	}
231
}