Completed
Push — master ( 2d3cc9...a2dba6 )
by Josh
28:54 queued 18:37
created

TemplateGenerator::getWrappedTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 12
cts 12
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 12
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2017 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Plugins\MediaEmbed\Configurator;
9
10
use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
11
12
abstract class TemplateGenerator
13
{
14
	/**
15
	* @var array Attributes used to generate current template
16
	*/
17
	protected $attributes;
18
19
	/**
20
	* @var array Default attributes
21
	*/
22
	protected $defaultAttributes = [
23
		'height'         => 360,
24
		'padding-height' => 0,
25
		'style'          => [],
26
		'width'          => 640
27
	];
28
29
	/**
30
	* Build the template representing the embedded content
31
	*
32
	* @return string
33
	*/
34
	abstract protected function getContentTemplate();
35
36
	/**
37
	* Build a template based on a list of attributes
38
	*
39
	* @param  array  $attributes
40
	* @return string
41
	*/
42 13
	public function getTemplate(array $attributes)
43
	{
44 13
		$this->attributes = $attributes + $this->defaultAttributes;
45
46 13
		return ($this->needsWrapper()) ? $this->getWrappedTemplate() : $this->getUnwrappedTemplate();
47
	}
48
49
	/**
50
	* Format an attribute value to be used in an XPath expression
51
	*
52
	* @param  string $expr Original value
53
	* @return string       Formatted value
54
	*/
55 7
	protected function expr($expr)
56
	{
57 7
		$expr = trim($expr, '{}');
58
59 7
		return (preg_match('(^[@$]?[-\\w]+$)D', $expr)) ? $expr : "($expr)";
60
	}
61
62
	/**
63
	* Generate xsl:attributes elements from an array
64
	*
65
	* @param  array  $attributes Array of [name => value] where value can be XSL code
66
	* @return string             XSL source
67
	*/
68 11
	protected function generateAttributes(array $attributes)
69
	{
70 11
		if (isset($attributes['style']) && is_array($attributes['style']))
71 11
		{
72 11
			$attributes['style'] = $this->generateStyle($attributes['style']);
73 11
		}
74
75 11
		ksort($attributes);
76 11
		$xsl = '';
77 11
		foreach ($attributes as $attrName => $attrValue)
78
		{
79 11
			$innerXML = (strpos($attrValue, '<xsl:') !== false) ? $attrValue : AVTHelper::toXSL($attrValue);
80
81 11
			$xsl .= '<xsl:attribute name="' . htmlspecialchars($attrName, ENT_QUOTES, 'UTF-8') . '">' . $innerXML . '</xsl:attribute>';
82 11
		}
83
84 11
		return $xsl;
85
	}
86
87
	/**
88
	* Generate a CSS declaration based on an array of CSS properties
89
	*
90
	* @param  array  $properties Property name => property value
91
	* @return string
92
	*/
93 11
	protected function generateStyle(array $properties)
94
	{
95 11
		ksort($properties);
96
97 11
		$style = '';
98 11
		foreach ($properties as $name => $value)
99
		{
100 11
			$style .= $name . ':' . $value . ';';
101 11
		}
102
103 11
		return trim($style, ';');
104
	}
105
106
	/**
107
	* Generate and return the padding declaration used in the responsive wrapper
108
	*
109
	* @return string
110
	*/
111 7
	protected function getResponsivePadding()
112
	{
113 7
		$height        = $this->expr($this->attributes['height']);
114 7
		$paddingHeight = $this->expr($this->attributes['padding-height']);
115 7
		$width         = $this->expr($this->attributes['width']);
116
117
		// Create the padding declaration for the fixed ratio
118 7
		$css = 'padding-bottom:<xsl:value-of select="100*(' . $height . '+' . $paddingHeight . ')div' . $width . '"/>%';
119
		
120
		// Add the padding declaration for the computed ratio if applicable
121 7
		if (!empty($this->attributes['padding-height']))
122 7
		{
123
			// NOTE: there needs to be whitespace around tokens in calc()
124 2
			$css .= ';padding-bottom:calc(<xsl:value-of select="100*' . $height . ' div' . $width . '"/>% + ' . $paddingHeight . 'px)';
125 2
		}
126
127
		// If the width is dynamic, use a conditional to protect against divisions by zero
128 7
		if (strpos($width, '@') !== false)
129 7
		{
130 1
			$css = '<xsl:if test="@width&gt;0">' . $css . '</xsl:if>';
131 1
		}
132
133 7
		return $css;
134
	}
135
136
	/**
137
	* Generate and return a responsive template for the embedded content
138
	*
139
	* @return string
140
	*/
141 6
	protected function getUnwrappedTemplate()
142
	{
143 6
		$this->attributes['style']['width']     = '100%';
144 6
		$this->attributes['style']['height']    = $this->attributes['height'] . 'px';
145 6
		$this->attributes['style']['max-width'] = '100%';
146
147 6
		if (isset($this->attributes['max-width']))
148 6
		{
149 1
			$this->attributes['style']['max-width'] = $this->attributes['max-width'] . 'px';
150 1
		}
151 5
		elseif ($this->attributes['width'] !== '100%')
152
		{
153 4
			$property = ($this->hasDynamicWidth()) ? 'width' : 'max-width';
154 4
			$this->attributes['style'][$property] = $this->attributes['width'] . 'px';
155 4
		}
156
157 6
		if ($this->attributes['style']['width'] === $this->attributes['style']['max-width'])
158 6
		{
159 1
			unset($this->attributes['style']['max-width']);
160 1
		}
161
162 6
		return $this->getContentTemplate();
163
	}
164
165
	/**
166
	* Generate and return a template for the embedded content, complete with a responsive wrapper
167
	*
168
	* @return string
169
	*/
170 7
	protected function getWrappedTemplate()
171
	{
172 7
		$this->attributes['style']['width']    = '100%';
173 7
		$this->attributes['style']['height']   = '100%';
174 7
		$this->attributes['style']['position'] = 'absolute';
175 7
		$this->attributes['style']['left'] = '0';
176
177 7
		$outerStyle = 'display:inline-block;width:100%;max-width:' . $this->attributes['width'] . 'px';
178 7
		$innerStyle = 'overflow:hidden;position:relative;' . $this->getResponsivePadding();
179
180 7
		$template  = '<div>' . $this->generateAttributes(['style' => $outerStyle]);
181 7
		$template .= '<div>' . $this->generateAttributes(['style' => $innerStyle]);
182 7
		$template .= $this->getContentTemplate();
183 7
		$template .= '</div></div>';
184
185 7
		return $template;
186
	}
187
188
	/**
189
	* Test whether current template has a dynamic height
190
	*
191
	* @return bool
192
	*/
193 9
	protected function hasDynamicHeight()
194
	{
195 9
		return (isset($this->attributes['onload']) && strpos($this->attributes['onload'], '.height') !== false);
196
	}
197
198
	/**
199
	* Test whether current template has a dynamic width
200
	*
201
	* @return bool
202
	*/
203 4
	protected function hasDynamicWidth()
204
	{
205 4
		return (isset($this->attributes['onload']) && strpos($this->attributes['onload'], '.width') !== false);
206
	}
207
208
	/**
209
	* Merge two array of attributes
210
	*
211
	* @param  array $defaultAttributes
212
	* @param  array $newAttributes
213
	* @return array
214
	*/
215 9
	protected function mergeAttributes(array $defaultAttributes, array $newAttributes)
216
	{
217 9
		$attributes = array_merge($defaultAttributes, $newAttributes);
218 9
		if (isset($defaultAttributes['style'], $newAttributes['style']))
219 9
		{
220
			// Re-add the default attributes that were lost (but not replaced) in the merge
221 9
			$attributes['style'] += $defaultAttributes['style'];
222 9
		}
223
224 9
		return $attributes;
225
	}
226
227
	/**
228
	* Test whether current template needs a wrapper to be responsive
229
	*
230
	* @return bool
231
	*/
232 11
	protected function needsWrapper()
233
	{
234 11
		return ($this->attributes['width'] !== '100%' && !$this->hasDynamicHeight());
235
	}
236
}