GenericHTMLConverter::fillOptionalOptions()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.2
c 0
b 0
f 0
cc 4
eloc 4
nc 3
nop 1
1
<?php
2
3
namespace OpenMindParser\Converters\HTML;
4
5
use OpenMindParser\Converters\Model\AbstractConverter;
6
use OpenMindParser\Models\Document;
7
use OpenMindParser\Models\Node;
8
use \DOMDocument;
9
use \InvalidArgumentException;
10
11
/*A class to convert a document tree (object Document) in a HTML tree following few options.*/
12
class GenericHTMLConverter extends AbstractConverter
13
{ 
0 ignored issues
show
Coding Style introduced by
The opening class brace should be on a newline by itself.
Loading history...
14
	/**
15
	 * String MAIN_TAG_KEY : An option key name for the list of HTML tags to use.
16
	 */
17
	const MAIN_TAG_KEY = 'tags';
18
	/**
19
	 * String TAG_KEY : An option key name for the HTML tag to use.
20
	 */
21
	const TAG_KEY = 'tag';
22
	/**
23
	 * String ATTRIBUTES_KEY : An option key name for the HTML attributes to use on the tag.
24
	 */
25
	const ATTRIBUTES_KEY = 'attributes'; 
26
	/**
27
	 * String MAIN_TAG_MERGE_STYLE : An option key name for a boolean to choose if document fonts and colors will be used in HTML rendering (true by default).
28
	 */
29
	const MAIN_TAG_MERGE_STYLE = 'merge_style'; 
30
	/**
31
	 * String MAIN_TAG_MERGE_STYLE : An option key name for a boolean to choose if texts set as bold and/or italic in the document will be set as it.
32
	 */
33
	const MAIN_TAG_MERGE_DECORATION = 'merge_decoration'; 
34
	/**
35
	 * String MAIN_ICON_KEY : An option key name for the icon tag to use.
36
	 */
37
	const MAIN_ICON_KEY = 'icon'; 
38
	/**
39
	 * String DISPLAY_ICON_KEY : A boolean to determine if for nodes with an icon, the icon will be added in a img tag before the text.
40
	 */
41
	 const DISPLAY_ICON_KEY = 'display';
42
	/**
43
	 * String PATH_ICON_KEY : Key for options to generate the icon path.
44
	 */
45
	 const PATH_ICON_KEY = 'path';
46
	/**
47
	 * String CALLBACK_PATH_ICON_KEY : A callback to generate the icon uri as wished. 
48
	 * Its first parameter is the icon full name (with extension (i.e : 'button_ok.png')).
49
	 * Signature : function($fullName, $options = null);
50
	 */
51
	 const CALLBACK_PATH_ICON_KEY = 'callback';
52
	/**
53
	 * String OPTIONS_PATH_ICON_KEY : An array of options given as a second parameter to the callback.
54
	 */
55
	 const OPTIONS_PATH_ICON_KEY = 'options';
56
	
57
	/**
58
	 * Call the conversion over the Document instance.
59
	 * 
60
	 * @var Document $document : The document instance to convert.
61
	 * @var Array $options : The options for the conversion. 
62
	 * It must follow this filling : 
63
	 * [
64
	 * 		MAIN_TAG_KEY              =>
65
	 * 			[
66
	 * 				TAG_KEY        => 'First wrapping HTML tag to use (ul, div, ...)',
67
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
68
	 * 			],
69
	 * 			[
70
	 * 				TAG_KEY        => 'Second wrapping HTML tag to use (ul, div, ...)',
71
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
72
	 * 			],
73
	 * 			[
74
	 * 				TAG_KEY        => 'Third wrapping HTML tag to use (ul, div, ...) (optionnal)',
75
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
76
	 * 			],
77
	 * 		MAIN_TAG_MERGE_STYLE      =>  A boolean to choose if document fonts and colors will be used in HTML rendering (true by default),
78
	 * 		MAIN_TAG_MERGE_DECORATION =>  A boolean to choose if texts set as bold and/or italic in the document will be set as it. (true by default),
79
	 * 		MAIN_ICON_KEY        => [
80
	 * 			DISPLAY_ICON_KEY => boolean determining if the icons must be displayed or not,
81
	 * 			PATH_ICON_KEY    => [
82
	 * 				'CALLBACK_PATH_ICON_KEY' => 'the callback to use to determine uri. Its first parameter will be the file full name, and the second an optional array with additional parametrs.',
83
	 * 				'OPTIONS_PATH_ICON_KEY'  => [array of options for the callback],
84
	 * 			],
85
	 * 		],
86
	 * ]. The first two are mandatory, and the style stored in the Node instance (color, font name and font size) will be applied on the second tag).
87
	 * 
88
	 * @return DOMDocument $domDocument : The DOMDocument instance created with the HTML.
89
	 */
90
	protected function convertFromDocumentInstance(Document $document, array $options) {
91
		$domDocument = new DOMDocument();
92
		
93
		$domDocument->appendChild($this->buildHTMLTreeFromNode($domDocument, $document->getRootNode(), $options));
94
		
95
		return $domDocument;
96
	}
97
	
98
	/**
99
	 * Perform the conversion over the Document instance. 
100
	 * Each text will be wrap in at least two HTML tags (<ul><li>, <div><span>, ...) and at most three tags (<ul><li><span>, ...).
101
	 * The style stored in the Node instance (color, font name and font size) will be applied on the second tag and override the "style" attribute if one is given.
102
	 * 
103
	 * @var DOMDocument $document : The document instance to convert.
104
	 * @var Array $options : The options for the conversion. 
105
	 * It must follow this filling : 
106
	 * [
107
	 * 		MAIN_TAG_KEY              =>
108
	 * 			[
109
	 * 				TAG_KEY        => 'First wrapping HTML tag to use (ul, div, ...)',
110
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
111
	 * 			],
112
	 * 			[
113
	 * 				TAG_KEY        => 'Second wrapping HTML tag to use (ul, div, ...)',
114
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
115
	 * 			],
116
	 * 			[
117
	 * 				TAG_KEY        => 'Third wrapping HTML tag to use (ul, div, ...) (optionnal)',
118
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
119
	 * 			],
120
	 * 		MAIN_TAG_MERGE_STYLE      =>  A boolean to choose if document fonts and colors will be used in HTML rendering (true by default),
121
	 * 		MAIN_TAG_MERGE_DECORATION =>  A boolean to choose if texts set as bold and/or italic in the document will be set as it. (true by default),
122
	 * 		MAIN_ICON_KEY        => [
123
	 * 			DISPLAY_ICON_KEY => boolean determining if the icons must be displayed or not,
124
	 * 			PATH_ICON_KEY    => [
125
	 * 				'CALLBACK_PATH_ICON_KEY' => 'the callback to use to determine uri. Its first parameter will be the file full name, and the second an optional array with additional parametrs.',
126
	 * 				'OPTIONS_PATH_ICON_KEY'  => [array of options for the callback],
127
	 * 			],
128
	 * 		],
129
	 * ]. The first two are mandatory.
130
	 * 
131
	 * @return \DOMElement $domDocument : The DOMDocument instance created with the HTML.
132
	 */
133
	private function buildHTMLTreeFromNode(DOMDocument $document, Node $node, array $options) {
134
		//Set optional values
135
		$this->fillOptionalOptions($options);
136
		
137
		//Create the first wrapping DOMElement.
138
		$domElementA = $this->buildElement($document, $options[self::MAIN_TAG_KEY][0]);
139
		
140
		//Merge font size and font family to add inside the second wrapping DOMElement.
141
		if($options[self::MAIN_TAG_MERGE_STYLE]) {
142
			$options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY] = array_merge(
143
				$options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY], 
144
				[
145
					'style' => 'color:'.$node->getColor().';'.
146
							   ($node->getFontName() ? 'font-family:'.$node->getFontName().';' : '').
147
							   ($node->getFontSize() ? 'font-size:'.$node->getFontSize().';' : ''),
148
				]
149
			);
150
		}
151
		
152
		//Add the node document id to the attributes.
153
		$options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY] = array_merge($options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY], ['id' => $node->getId()]);
154
		
155
		//Create the second wrapping DOMElement whom will be append to the first one
156
		$domElementB = $this->buildElement($document, $options[self::MAIN_TAG_KEY][1]);
157
		
158
		//Set the icon in an img tag whom will be wrapped in the second DOMElement.
159
		if(!empty($node->getIcon()) && $options[self::MAIN_ICON_KEY][self::DISPLAY_ICON_KEY]) {
160
			$icon = $node->getIcon();
161
			$domElementImg = $document->createElement('img');
162
			
163
			$iconUri = $icon->getShortUri();
164
			if(array_key_exists(self::PATH_ICON_KEY, $options[self::MAIN_ICON_KEY])) {
165
				if(!is_callable($callback = $options[self::MAIN_ICON_KEY][self::PATH_ICON_KEY][self::CALLBACK_PATH_ICON_KEY])) {
166
					throw new InvalidArgumentException('The argument with the key "'.self::CALLBACK_PATH_ICON_KEY.'" (self::CALLBACK_PATH_ICON_KEY) must be a valid callback.');
167
				}
168
				
169
				$callBackoptions = array_key_exists(self::OPTIONS_PATH_ICON_KEY, $options[self::MAIN_ICON_KEY][self::PATH_ICON_KEY]) ?
170
					$options[self::MAIN_ICON_KEY][self::PATH_ICON_KEY][self::OPTIONS_PATH_ICON_KEY] :
171
					null;
172
				
173
				$iconUri = $callback($icon->getFullName(), $callBackoptions);
174
			}
175
			
176
			$domElementImg->setAttribute('src', $iconUri);
177
			$domElementImg->setAttribute('alt', $icon->getName());
178
			$domElementB->appendChild($domElementImg);
179
		}
180
		
181
		//Define the TextNode to append to the current wrapping elements.
182
		$text = $document->createTextNode($node->getText());
183
		
184
		//Bold an italic in old HTML way in order not to spread it to all children.
185
		if($options[self::MAIN_TAG_MERGE_DECORATION]) {
186
			if($node->isBold()) {
187
				$bTag = $document->createElement('b');
188
				$bTag->appendChild($text);
189
				$text = $bTag;
190
			}
191
			if($node->isItalic()) {
192
				$iTag = $document->createElement('i');
193
				$iTag->appendChild($text);
194
				$text = $iTag;
195
			}
196
		}
197
		
198
		//Append text node (real one or the one already wrapped inside <b> and/or <i> tags)
199
		if(isset($options[self::MAIN_TAG_KEY][2])) {
200
			$domElementC = $this->buildElement($document, $options[self::MAIN_TAG_KEY][2]);
201
			$domElementC->appendChild($text);
202
			$domElementB->appendChild($domElementC);
203
		} else {
204
			$domElementB->appendChild($text);
205
		}
206
		
207
		$domElementA->appendChild($domElementB);
208
		
209
		foreach($node->getChildren() as $child) {
210
			$domElementB->appendChild($this->buildHTMLTreeFromNode($document, $child, $options));
211
		}
212
		
213
		return $domElementA;
214
	}
215
	
216
	/**
217
	 * Set optionals values for merging style and decoration if they are not.
218
	 * 
219
	 * @param array &$options : The options array passed as reference.
220
	 */
221
	private function fillOptionalOptions(array &$options) {
222
		foreach([self::MAIN_TAG_MERGE_DECORATION, self::MAIN_TAG_MERGE_STYLE] as $key) {
223
			if(!array_key_exists($key, $options) || !is_bool($options[$key])) {
224
				$options[$key] = true;
225
			}
226
		}
227
	}
228
	
229
	/**
230
	 * Build the HTML tag following its given tag name and attributes stored in $description array.
231
	 * 
232
	 * @param DOMDocument $document : The current DOMDocument in creation with the HTML tree.
233
	 * @param array $description : The array describing the tag to create.
234
	 * 
235
	 * @return \DOMElement $domElement : The created DOMElement.
236
	 */
237
	private function buildElement(DOMDocument $document, array $description) {
238
		$domElement = $document->createElement($description[self::TAG_KEY]);
239
		foreach($description[self::ATTRIBUTES_KEY] as $name => $attribute) {
240
			$domElement->setAttribute($name, $attribute);
241
		}
242
		
243
		return $domElement;
244
	}
245
}