Completed
Push — master ( f654a1...27d1b6 )
by Josh
19:17
created

TemplateGenerator::hasDynamicWidth()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 2
rs 10
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2016 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
		$prepend = $append = '';
47 13
		if ($this->needsWrapper())
48 13
		{
49 7
			$this->attributes['style']['width']    = '100%';
50 7
			$this->attributes['style']['height']   = '100%';
51 7
			$this->attributes['style']['position'] = 'absolute';
52 7
			$this->attributes['style']['left'] = '0';
53
54 7
			$outerStyle = 'display:inline-block;width:100%;max-width:' . $this->attributes['width'] . 'px';
55 7
			$innerStyle = 'overflow:hidden;position:relative;' . $this->getResponsivePadding();
56
57 7
			$prepend .= '<div>' . $this->generateAttributes(['style' => $outerStyle]);
58 7
			$prepend .= '<div>' . $this->generateAttributes(['style' => $innerStyle]);
59 7
			$append  .= '</div></div>';
60 7
		}
61
		else
62
		{
63 6
			$this->attributes['style']['width']  = '100%';
64 6
			$this->attributes['style']['height'] = $this->attributes['height'] . 'px';
65
66 6
			if (isset($this->attributes['max-width']))
67 6
			{
68 1
				$this->attributes['style']['max-width'] = $this->attributes['max-width'] . 'px';
69 1
			}
70 5
			elseif ($this->attributes['width'] !== '100%')
71
			{
72 4
				$property = ($this->hasDynamicWidth()) ? 'width' : 'max-width';
73 4
				$this->attributes['style'][$property] = $this->attributes['width'] . 'px';
74 4
			}
75
		}
76
77 13
		return $prepend . $this->getContentTemplate() . $append;
78
	}
79
80
	/**
81
	* Format an attribute value to be used in an XPath expression
82
	*
83
	* @param  string $expr Original value
84
	* @return string       Formatted value
85
	*/
86 7
	protected function expr($expr)
87
	{
88 7
		$expr = trim($expr, '{}');
89
90 7
		return (preg_match('(^[@$]?[-\\w]+$)D', $expr)) ? $expr : "($expr)";
91
	}
92
93
	/**
94
	* Generate and return the padding declaration used in the responsive wrapper
95
	*
96
	* @return string
97
	*/
98 7
	protected function getResponsivePadding()
99
	{
100 7
		$height        = $this->expr($this->attributes['height']);
101 7
		$paddingHeight = $this->expr($this->attributes['padding-height']);
102 7
		$width         = $this->expr($this->attributes['width']);
103
104
		// Create the padding declaration for the fixed ratio
105 7
		$css = 'padding-bottom:<xsl:value-of select="100*(' . $height . '+' . $paddingHeight . ')div' . $width . '"/>%';
106
		
107
		// Add the padding declaration for the computed ratio if applicable
108 7
		if (!empty($this->attributes['padding-height']))
109 7
		{
110
			// NOTE: there needs to be whitespace around tokens in calc()
111 2
			$css .= ';padding-bottom:calc(<xsl:value-of select="100*' . $height . ' div' . $width . '"/>% + ' . $paddingHeight . 'px)';
112 2
		}
113
114
		// If the width is dynamic, use a conditional to protect against divisions by zero
115 7
		if (strpos($width, '@') !== false)
116 7
		{
117 1
			$css = '<xsl:if test="@width&gt;0">' . $css . '</xsl:if>';
118 1
		}
119
120 7
		return $css;
121
	}
122
123
	/**
124
	* Generate xsl:attributes elements from an array
125
	*
126
	* @param  array  $attributes Array of [name => value] where value can be XSL code
127
	* @return string             XSL source
128
	*/
129 11
	protected function generateAttributes(array $attributes)
130
	{
131 11
		if (isset($attributes['style']) && is_array($attributes['style']))
132 11
		{
133 11
			$attributes['style'] = $this->generateStyle($attributes['style']);
134 11
		}
135
136 11
		ksort($attributes);
137 11
		$xsl = '';
138 11
		foreach ($attributes as $attrName => $attrValue)
139
		{
140 11
			$innerXML = (strpos($attrValue, '<xsl:') !== false) ? $attrValue : AVTHelper::toXSL($attrValue);
141
142 11
			$xsl .= '<xsl:attribute name="' . htmlspecialchars($attrName, ENT_QUOTES, 'UTF-8') . '">' . $innerXML . '</xsl:attribute>';
143 11
		}
144
145 11
		return $xsl;
146
	}
147
148
	/**
149
	* Generate a CSS declaration based on an array of CSS properties
150
	*
151
	* @param  array  $properties Property name => property value
152
	* @return string
153
	*/
154 11
	protected function generateStyle(array $properties)
155
	{
156 11
		ksort($properties);
157
158 11
		$style = '';
159 11
		foreach ($properties as $name => $value)
160
		{
161 11
			$style .= $name . ':' . $value . ';';
162 11
		}
163
164 11
		return trim($style, ';');
165
	}
166
167
	/**
168
	* Test whether current template has a dynamic height
169
	*
170
	* @return bool
171
	*/
172 9
	protected function hasDynamicHeight()
173
	{
174 9
		return (isset($this->attributes['onload']) && strpos($this->attributes['onload'], '.height') !== false);
175
	}
176
177
	/**
178
	* Test whether current template has a dynamic width
179
	*
180
	* @return bool
181
	*/
182 4
	protected function hasDynamicWidth()
183
	{
184 4
		return (isset($this->attributes['onload']) && strpos($this->attributes['onload'], '.width') !== false);
185
	}
186
187
	/**
188
	* Merge two array of attributes
189
	*
190
	* @param  array $defaultAttributes
191
	* @param  array $newAttributes
192
	* @return array
193
	*/
194 9
	protected function mergeAttributes(array $defaultAttributes, array $newAttributes)
195
	{
196 9
		$attributes = array_merge($defaultAttributes, $newAttributes);
197 9
		if (isset($defaultAttributes['style'], $newAttributes['style']))
198 9
		{
199
			// Re-add the default attributes that were lost (but not replaced) in the merge
200 9
			$attributes['style'] += $defaultAttributes['style'];
201 9
		}
202
203 9
		return $attributes;
204
	}
205
206
	/**
207
	* Test whether current template needs a wrapper to be responsive
208
	*
209
	* @return bool
210
	*/
211 11
	protected function needsWrapper()
212
	{
213 11
		return ($this->attributes['width'] !== '100%' && !$this->hasDynamicHeight());
214
	}
215
}